Kaynağa Gözat

Merge branch 'smslogin' into dev

shish 1 ay önce
ebeveyn
işleme
37ae115c92

+ 183 - 0
app-ghs/controllers/AuthController.php

@@ -15,6 +15,7 @@ use common\components\httpUtil;
 use common\components\imgUtil;
 use common\components\jwt;
 use common\components\noticeUtil;
+use common\components\sms;
 use common\components\util;
 use Yii;
 use common\components\stringUtil;
@@ -805,4 +806,186 @@ class AuthController extends PublicController
         util::success(['html' => $html]);
     }
 
+    /**
+     * 【用途】发送登录短信验证码
+     * 【为什么】供短信登录功能获取验证码,并包含防刷安全限制(单手机号60秒锁、每日上限5条)
+     */
+    public function actionSendLoginSms()
+    {
+        $getParams = Yii::$app->request->get();
+        $postParams = Yii::$app->request->post();
+        $allParams = array_merge($getParams, $postParams);
+        $mobile = $allParams['mobile'] ?? '';
+
+        if (!stringUtil::isMobile($mobile)) {
+            util::fail('请填写正确手机号');
+        }
+
+        // 1. 安全检查:单手机号 60 秒防刷
+        $lockKey = 'LOGIN_SMS_LOCK_' . $mobile;
+        if (Yii::$app->redis->executeCommand('GET', [$lockKey])) {
+            util::fail('发送过于频繁,请稍后再试');
+        }
+
+        // 2. 安全检查:单手机号每日上限 5 条
+        $dayKey = 'LOGIN_SMS_COUNT_' . date('Ymd') . '_' . $mobile;
+        $sendCount = (int)Yii::$app->redis->executeCommand('GET', [$dayKey]);
+        if ($sendCount >= 5) {
+            util::fail('该手机号今日获取验证码已达上限');
+        }
+
+        // 3. 生成 6 位随机验证码并保存至 Redis(有效期 5 分钟)
+        $code = (string)rand(100000, 999999);
+        $codeCacheKey = 'LOGIN_SMS_CODE_' . $mobile;
+        Yii::$app->redis->executeCommand('SETEX', [$codeCacheKey, 300, $code]);
+
+        // 4. 调用系统自带的限流发送组件 (内置了 IP 60秒锁和每日15条限制)
+        $minute = 5;
+        sms::send($mobile . ',' . $code . ',' . $minute, '登录验证码:{$var},{$var}分钟内有效');
+
+        // 5. 写入单手机号限制锁
+        Yii::$app->redis->executeCommand('SETEX', [$lockKey, 60, '1']);
+        Yii::$app->redis->executeCommand('SETEX', [$dayKey, 86400, $sendCount + 1]);
+
+        util::complete('验证码发送成功');
+    }
+
+    /**
+     * 【用途】短信验证码登录
+     * 【为什么】提供免密码快捷登录方式,验证通过后自动签发 JWT Token 并返回商家后台所需的所有初始化数据
+     */
+    public function actionSmsLogin()
+    {
+        $getParams = Yii::$app->request->get();
+        $postParams = Yii::$app->request->post();
+        $allParams = array_merge($getParams, $postParams);
+        $mobile = $allParams['mobile'] ?? '';
+        $code = $allParams['code'] ?? '';
+
+        if (empty($mobile) || empty($code)) {
+            util::fail('手机号和验证码不能为空');
+        }
+
+        if (!stringUtil::isMobile($mobile)) {
+            util::fail('请填写正确手机号');
+        }
+
+        // 1. 校验验证码
+        $codeCacheKey = 'LOGIN_SMS_CODE_' . $mobile;
+        $savedCode = Yii::$app->redis->executeCommand('GET', [$codeCacheKey]);
+
+        if (empty($savedCode) || $savedCode !== $code) {
+            util::fail('验证码错误或已过期');
+        }
+
+        // 2. 验证通过,立即销毁验证码以确保一次性使用
+        Yii::$app->redis->executeCommand('DEL', [$codeCacheKey]);
+
+        // 3. 查找用户并执行登录
+        $admin = AdminService::getByCondition(['mobile' => $mobile], true);
+        if (empty($admin)) {
+            util::fail('请先注册哦...');
+        }
+
+        $openShop = $admin->openGhsShop ?? 1;
+        $currentShopId = $admin->currentGhsShopId ?? 0;
+        if (empty($currentShopId)) {
+            if ($openShop == 2) {
+                util::fail('审核中');
+            }
+            util::fail('请先注册');
+        }
+        $currentShop = \bizGhs\shop\classes\ShopClass::getById($currentShopId, true);
+        if (empty($currentShop)) {
+            util::fail('没有找到门店71');
+        }
+        $mainId = $currentShop->mainId ?? 0;
+        $adminId = $admin->id;
+        $shopAdmin = ShopAdminService::getByCondition(['mainId' => $mainId, 'adminId' => $adminId], true);
+        if (empty($shopAdmin)) {
+            util::fail('您没有权限');
+        }
+        if ($shopAdmin->delStatus == 1) {
+            util::fail('您没有权限哦');
+        }
+        if ($shopAdmin->status == 0) {
+            util::fail("您的账号已被冻结");
+        }
+        $token = jwt::getNewToken($adminId);
+        $shopAdminId = $shopAdmin->id ?? 0;
+        //是否有切换门店的权限
+        $switchShop = \biz\shop\classes\ShopAdminClass::hasSwitchShopRight($shopAdmin);
+        //1没有开店 2已申请待审核 3已开店
+        $openShop = $admin['openGhsShop'] ?? 1;
+        $showDemo = 1;
+        $shop = ShopClass::getById($currentShopId, true);
+        $skCustomId = $shop->skCustomId ?? 0;
+        $apiHost = Yii::$app->params['ghsHost'];
+        $imgUploadApi = $apiHost . '/upload/save-file';
+        //使用手册
+        $cacheKey = 'close_book_' . $shopAdminId;
+        $hasClose = Yii::$app->redis->executeCommand('GET', [$cacheKey]);
+        $showBook = !empty($hasClose) ? 0 : 1;
+
+        $cacheKey = 'has_hit_navigate_' . $shopAdminId;
+        $hasHit = Yii::$app->redis->executeCommand('GET', [$cacheKey]);
+        $hasHitNavigate = !empty($hasHit) ? 1 : 0;
+
+        $ghsUpgrading = getenv('GHS_UPGRADING') == false ? 0 : getenv('GHS_UPGRADING');
+        if ($ghsUpgrading == 1) {
+            $allowShopAdminIdsStr = getenv('GHS_UPGRADE_ALLOW_SHOP_ADMIN_IDS') ?: '';
+            $allowShopAdminIds = !empty($allowShopAdminIdsStr) ? explode(',', $allowShopAdminIdsStr) : [];
+            if (!in_array($shopAdminId, $allowShopAdminIds)) {
+                util::fail('系统升级中,稍后再试');
+            }
+        }
+
+        $lookAllShop = 0;
+        if (getenv('YII_ENV') == 'production') {
+            $couldLookAllShop = dict::getDict('couldLookAllShop');
+            if (in_array($adminId, $couldLookAllShop)) {
+                $lookAllShop = 1;
+            }
+        } else {
+            $lookAllShop = 1;
+        }
+
+        //惠雅鲜花员工不能看收入情况
+        if (in_array($adminId, [2366, 2812, 4004])) {
+            $shopAdmin->super = 0;
+        }
+
+        $labelList = LabelClass::getMyUnUseLabelList($mainId);
+
+        //把设备状态更新为登录
+        if (isset($allParams['deviceId']) && $allParams['deviceId'] != '') {
+            $device =  \bizGhs\device\classes\GhsDeviceClass::getByCondition(['shopId'=>$currentShopId, 'deviceId'=>$allParams['deviceId']], true);
+            if ($device) {
+                $device->status = 1;
+                $device->save();
+            }
+        }
+
+        //注意这里的输出内容有多个地方一样,要修改需要同步修改,请搜索关键词loginOK!!!!!!!!
+        util::success([
+            'token' => $token,
+            'admin' => $admin,
+            'shopAdminId' => $shopAdminId,
+            'shopId' => $currentShopId,
+            'switchShop' => $switchShop,
+            'openShop' => $openShop,
+            'showDemo' => $showDemo,
+            //有小程序新版本提示更新
+            'update' => 0,
+            'skCustomId' => $skCustomId,
+            'apiHost' => $apiHost,
+            'imgUploadApi' => $imgUploadApi,
+            'showBook' => $showBook,
+            'hasHitNavigate' => $hasHitNavigate,
+            'staff' => $shopAdmin,
+            'lookAllShop' => $lookAllShop,
+            'labelList' => $labelList,
+        ]);
+    }
+
 }

+ 3 - 2
app-ghs/controllers/ConsoleController.php

@@ -176,6 +176,7 @@ class ConsoleController extends BaseController
         //$notice[] = ['title' => '如果你发现一个问题很重要,一直没给你处理,在小群多反应几次,用反应次数强调重要性,目前批发店多反应问题多,都在排队。', 'action' => '', 'page' => ''];
         //$notice[] = ['title' => '新功能:客户端和后台使用跑腿的流程', 'action' => '', 'page' => '/admin/book/detail?id=use_pt'];
         //$notice[] = ['title' => '分享订单打不开的解决办法', 'action' => '查看', 'page' => '/admin/book/detail?id=calc'];
+        //$notice[] = ['title' => '急事请连续打二次电话 15280215347', 'action' => '', 'page' => ''];
 
         $mainId = $this->mainId;
         $respond = StatSaleClass::profile($mainId);
@@ -314,7 +315,7 @@ class ConsoleController extends BaseController
 
         $warning = '';
         $warningUrl = '';
-        $warning = '挂账结账重大调整(已上线) 查看 >';
+        $warning = '挂账结账重大调整(已上线) 查看 >';
         $warningUrl = '/admin/notice/warning';
 
         util::success([
@@ -334,7 +335,7 @@ class ConsoleController extends BaseController
         ]);
     }
 
-    //常用初始化
+    //常用数据初始化
     public function actionInit()
     {
         $dict = dict::get();

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

@@ -114,7 +114,7 @@ class ConsoleController extends BaseController
         $lookMoney = \bizGhs\shop\classes\ShopAdminClass::lookMoneyPower($this->shopAdmin, $this->shop);
         $warning = '';
         $warningUrl = '';
-        $warning = '结账功能重大调整 查看 >';
+        $warning = '挂账和结账重大调整 查看 >';
         $warningUrl = '/admin/notice/warning';
 
         util::success([

+ 2 - 2
common/components/util.php

@@ -278,8 +278,8 @@ class util
      */
     public static function fail($msg = '操作失败')
     {
-        // Yii::info($msg);
-        Yii::info($msg . "\n调用堆栈:\n" . (new \Exception())->getTraceAsString(), __METHOD__);
+        Yii::info($msg);
+        //Yii::info($msg . "\n调用堆栈:\n" . (new \Exception())->getTraceAsString(), __METHOD__);
 
         $report = isset(Yii::$app->params['errorReport']) ? Yii::$app->params['errorReport'] : 0;
 

+ 164 - 32
console/controllers/ClearController.php

@@ -6,6 +6,8 @@ use biz\ghs\classes\GhsBackClass;
 use bizGhs\clear\classes\OrderCgClearClass;
 use bizGhs\clear\models\Clear;
 use bizGhs\custom\classes\CustomClass;
+use bizGhs\custom\classes\CustomBalanceChangeClass;
+use bizGhs\ghs\classes\GhsBalanceChangeClass;
 use bizGhs\custom\services\GhsRechargeSettleService;
 use bizGhs\ghs\classes\GhsClass;
 use bizGhs\ghs\models\Ghs;
@@ -141,45 +143,176 @@ class ClearController extends Controller
     {
         $list = GhsBackClass::getAllByCondition(['id>' => 0], null, '*', 'id', true);
         if (!empty($list)) {
-            foreach ($list as $ghs) {
-                $ownPtStyle = $ghs['ownPtStyle'] ?? 1;
-                $balance = $ghs['balance'] ?? 0;
-                $customId = $ghs['customId'] ?? 0;
-                if ($ownPtStyle == 1) {
-                    $custom = CustomClass::getById($customId, true);
-                    if (!empty($custom)) {
-                        $shopId = $ghs['shopId'] ?? 0;
-                        $shop = \bizGhs\shop\classes\ShopClass::getById($shopId, true);
-                        if (!empty($shop)) {
-                            $connection = Yii::$app->db;
-                            $transaction = $connection->beginTransaction();
-                            try {
-                                $staff = new \stdClass();
-                                $staff->id = 0;
-                                $staff->name = '';
-                                $params = [
-                                    'remark' => '系统升级恢复',
-                                    'rechargeType' => 0, // 0 代表正常充值
-                                ];
-                                // 模拟商家帮充并自动销挂账,payWay 设为 0 (线下微信)
-                                GhsRechargeSettleService::merchantRechargeWithAutoClear($custom, $balance, $shop, $staff, 0, $params);
-                                $transaction->commit();
-                                echo "customId: {$customId} 充值成功 {$balance} 元\n";
-                            } catch (\Exception $e) {
-                                $transaction->rollBack();
-                                echo "customId: {$customId} 充值失败: " . $e->getMessage() . "\n";
+            $connection = Yii::$app->db;
+            $transaction = $connection->beginTransaction();
+            try {
+                foreach ($list as $ghs) {
+                    $ownPtStyle = $ghs['ownPtStyle'] ?? 1;
+                    $balance = $ghs['balance'] ?? 0;
+                    $customId = $ghs['customId'] ?? 0;
+                    if($customId == 16440 || $customId == 46425){
+                        if ($ownPtStyle == 1) {
+                            $custom = CustomClass::getById($customId, true);
+                            if (!empty($custom)) {
+                                $shopId = $ghs['shopId'] ?? 0;
+                                $shop = \bizGhs\shop\classes\ShopClass::getById($shopId, true);
+                                if (!empty($shop)) {
+                                    $staff = new \stdClass();
+                                    $staff->id = 0;
+                                    $staff->name = '';
+                                    $params = [
+                                        'remark' => '系统升级恢复',
+                                        'rechargeType' => 0, // 0 代表正常充值
+                                    ];
+                                    // 模拟商家帮充并自动销挂账,payWay 设为 0 (线下微信)
+                                    GhsRechargeSettleService::merchantRechargeWithAutoClear($custom, $balance, $shop, $staff, 0, $params);
+                                    echo "customId: {$customId} 充值成功 {$balance} 元\n";
+                                } else {
+                                    echo "customId: {$customId} 找不到对应的 shop\n";
+                                }
+                            } else {
+                                echo "customId: {$customId} 找不到对应的 custom\n";
                             }
-                        } else {
-                            echo "customId: {$customId} 找不到对应的 shop\n";
                         }
-                    } else {
-                        echo "customId: {$customId} 找不到对应的 custom\n";
                     }
                 }
+                $transaction->commit();
+            } catch (\Exception $e) {
+                $transaction->rollBack();
+                echo "执行失败,整体回滚: " . $e->getMessage() . "\n";
             }
         }
     }
 
+    /**
+     * 安全回滚最新一次“系统升级恢复”产生的多余充值和销账数据
+     * 支持传参单个 customId 测试:php yii clear/rollback-latest-upgrade 25695
+     * 批量跑全部:php yii clear/rollback-latest-upgrade
+     */
+    public function actionRollbackLatestUpgrade($customId = 0)
+    {
+        $connection = Yii::$app->db;
+        $customId = intval($customId);
+        
+        // 1. 查找所有备注为 "系统升级恢复" 的充值流水 (按 ID 倒序,最新产生的在最前面)
+        $query = (new \yii\db\Query())
+            ->from('xhCustomRecharge')
+            ->where(['remark' => '系统升级恢复']);
+            
+        if ($customId > 0) {
+            $query->andWhere(['customId' => $customId]);
+        }
+        
+        $recharges = $query->orderBy('id DESC')->all();
+            
+        if (empty($recharges)) {
+            echo "没有找到 '系统升级恢复' 相关的充值记录,无需回滚。\n";
+            return;
+        }
+        
+        // 2. 按客户分组,找到每个客户绝对最新(最后一次运行)的那一笔充值
+        $latestRecharges = [];
+        foreach ($recharges as $r) {
+            $cid = $r['customId'];
+            if (!isset($latestRecharges[$cid])) {
+                $latestRecharges[$cid] = $r;
+            }
+        }
+        
+        if ($customId > 0) {
+            echo "正在回滚指定客户 ID: {$customId} 的最新一笔升级恢复数据...\n";
+        } else {
+            echo "共找到 " . count($latestRecharges) . " 个客户的最新充值记录,准备执行批量安全回滚...\n";
+        }
+        
+        $transaction = $connection->beginTransaction();
+        try {
+            foreach ($latestRecharges as $cr) {
+                $cId = intval($cr['customId']);
+                $amount = $cr['amount'];
+                $clearId = intval($cr['clearId']);
+                $crId = intval($cr['id']);
+                $ghsRechargeId = intval($cr['ghsRechargeId'] ?? 0);
+                
+                // 1) 备注说明:根据用户指令,xhGhs (供货商) 与 xhGhsCustom (客户) 的 balance 余额无需变动还原,因此这里不执行余额扣减。
+                
+                // 2) 如果这笔充值关联了自动销账(clearId > 0),精准回退订单待结和结账单状态
+                if ($clearId > 0) {
+                    $cgClears = \bizGhs\clear\classes\OrderCgClearClass::getAllByCondition(['clearId' => $clearId], null, '*', null, true);
+                    foreach ($cgClears as $cgClear) {
+                        $orderId = intval($cgClear->orderId);
+                        $cgId = intval($cgClear->cgId);
+                        $clearedAmount = $cgClear->amount;
+                        
+                        // a. 恢复批发端的订单未结挂账金额和状态 (xhOrder)
+                        if ($orderId > 0) {
+                            $order = \bizGhs\order\classes\OrderClass::getLockById($orderId);
+                            if (!empty($order)) {
+                                $order->remainDebtPrice = bcadd($order->remainDebtPrice, $clearedAmount, 2);
+                                $order->debt = 1; // 重新恢复为挂账状态
+                                if ($order->clearId == $clearId) {
+                                    $order->clearId = 0;
+                                    $order->clearTime = '0000-00-00 00:00:00';
+                                }
+                                $order->save(false);
+                            }
+                        }
+                        
+                        // b. 恢复零售花店端的采购单未结挂账金额和状态 (xhPurchase)
+                        $purchase = null;
+                        if ($cgId > 0) {
+                            $purchase = \bizHd\purchase\classes\PurchaseClass::getLockById($cgId);
+                        } elseif ($orderId > 0) {
+                            $purchase = \bizHd\purchase\classes\PurchaseClass::getByCondition(['saleId' => $orderId], true);
+                            if (!empty($purchase)) {
+                                $purchase = \bizHd\purchase\classes\PurchaseClass::getLockById($purchase->id);
+                            }
+                        }
+                        if (!empty($purchase)) {
+                            $purchase->remainDebtPrice = bcadd($purchase->remainDebtPrice, $clearedAmount, 2);
+                            $purchase->debt = 1; // 恢复挂账状态
+                            if ($purchase->clearId == $clearId) {
+                                $purchase->clearId = 0;
+                                $purchase->clearTime = '0000-00-00 00:00:00';
+                            }
+                            $purchase->save(false);
+                        }
+                        
+                        // 删除销账明细纪录
+                        $cgClear->delete();
+                    }
+                    
+                    // 将结账单(xhClear)置为已作废状态 (status = 3)
+                    $clearObj = \bizGhs\order\classes\OrderClearClass::getLockById($clearId);
+                    if (!empty($clearObj)) {
+                        $clearObj->status = 3; // 3 代表已作废/已取消
+                        $clearObj->save(false);
+                    }
+                }
+                
+                // 3) 删除本次充值产生的余额变动明细纪录
+                CustomBalanceChangeClass::deleteByCondition(['relateId' => $crId]);
+                if ($ghsRechargeId > 0) {
+                    GhsBalanceChangeClass::deleteByCondition(['relateId' => $ghsRechargeId]);
+                }
+                
+                // 4) 删除充值记录本身
+                $connection->createCommand()->delete('xhCustomRecharge', ['id' => $crId])->execute();
+                if ($ghsRechargeId > 0) {
+                    $connection->createCommand()->delete('xhGhsRecharge', ['id' => $ghsRechargeId])->execute();
+                }
+                
+                echo "【成功】客户 ID: {$cId} 多余的充值及 {$amount} 元的销账订单已完美退回并删除!\n";
+            }
+            
+            $transaction->commit();
+            echo "【全部完成】数据已成功安全回滚!\n";
+        } catch (\Exception $e) {
+            $transaction->rollBack();
+            echo "【错误】回滚失败,已安全整体撤销。报错原因: " . $e->getMessage() . "\n";
+        }
+    }
+
     public function actionGhsGhs()
     {
         ini_set('memory_limit', '2045M');
@@ -189,7 +322,6 @@ class ClearController extends Controller
         $query->from(Ghs::tableName());
         $query->where(['ownPtStyle' => 2]);
         foreach ($query->batch(500) as $ghsList) {
-
             $connection = Yii::$app->db;
             $transaction = $connection->beginTransaction();
             try {