Pārlūkot izejas kodu

Merge branch 'new-version' into feature/NC-45

# Conflicts:
#	backendApi/config/params.php
#	backendApi/modules/v1/models/LoginForm.php
zhangl 1 gadu atpakaļ
vecāks
revīzija
1f78b6a42c

+ 4 - 0
backendApi/config/urlManagerRules.php

@@ -19,6 +19,10 @@ return [
             'GET captcha' => 'captcha',
             'GET send-notice' => 'send-notice',
             'GET clear-login-failed-num' => 'clear-login-failed-num',
+            'GET open-backend-ip-filter' => 'open-backend-ip-filter',
+            'GET close-backend-ip-filter' => 'close-backend-ip-filter',
+            'GET open-member-ip-filter' => 'open-member-ip-filter',
+            'GET close-member-ip-filter' => 'close-member-ip-filter',
         ],
     ],
     [

+ 16 - 0
backendApi/modules/v1/controllers/BaseController.php

@@ -10,6 +10,7 @@ namespace backendApi\modules\v1\controllers;
 
 use common\helpers\Date;
 use common\helpers\Tool;
+use common\libs\IpFilter;
 use common\models\UserInfo;
 use common\models\UserSystem;
 use \Yii;
@@ -38,6 +39,21 @@ class BaseController extends \yii\rest\ActiveController {
     public function beforeAction($action) {
         $parentBeforeAction = parent::beforeAction($action);
 
+        $notFilterApi = [
+            'v1/oauth/login',
+            'v1/site/page-data',
+            'v1/oauth/send-email-code',
+            'v1/site/days-diff',
+            'v1/site/captcha',
+        ];
+
+        $request = Yii::$app->request;
+        if (\Yii::$app->redis->get('backend_ip_filter') && !Tool::checkArrayElementSubstringOfString($request->getUrl(), $notFilterApi)) {
+            if (!(new IpFilter())->checkIp('backend')) {
+                throw new \Exception('用户名或密码错误');
+            }
+        }
+
         // 增加的判断用户登录后未操作后的超时
         if (Yii::$app->getUser()->getUserInfo()){
             $adminId = Yii::$app->getUser()->getUserInfo()['id'];

+ 34 - 1
backendApi/modules/v1/controllers/SiteController.php

@@ -7,6 +7,7 @@
  */
 namespace backendApi\modules\v1\controllers;
 
+use common\helpers\LoggerTool;
 use common\helpers\snowflake\PageSnowFake;
 use common\models\DealType;
 use common\models\Period;
@@ -165,8 +166,40 @@ class SiteController extends BaseController
     {
         $adminName = \Yii::$app->request->get('adminName');
 
+        $loginFailNums = Yii::$app->redis->get('FAIL_NUMS:' . $adminName) ?? 0;
         \Yii::$app->redis->del('FAIL_NUMS:' . $adminName);
+        LoggerTool::error(sprintf('tmp_log_fail_nums_del, adminName: %s', $adminName));
+        $loginFailNumsNie = Yii::$app->redis->get('FAIL_NUMS:' . $adminName) ?? 0;
 
-        return static::notice(['data' => '登陆失败次数清空']);
+        return static::notice(['data' => sprintf('登陆失败次数清空. 失败次数:%d -> %d', $loginFailNums, $loginFailNumsNie)]);
     }
+
+    public function actionOpenBackendIpFilter()
+    {
+        \Yii::$app->redis->set('backend_ip_filter', 1);
+
+        return static::notice(['data' => '开启后台IP过滤']);
+    }
+
+    public function actionCloseBackendIpFilter()
+    {
+        \Yii::$app->redis->del('backend_ip_filter');
+
+        return static::notice(['data' => '关闭后台IP过滤']);
+    }
+
+    public function actionOpenMemberIpFilter()
+    {
+        \Yii::$app->redis->set('member_ip_filter', 1);
+
+        return static::notice(['data' => '开启会员IP过滤']);
+    }
+
+    public function actionCloseMemberIpFilter()
+    {
+        \Yii::$app->redis->del('member_ip_filter');
+
+        return static::notice(['data' => '关闭会员IP过滤']);
+    }
+
 }

+ 16 - 0
common/helpers/Tool.php

@@ -588,4 +588,20 @@ class Tool {
 
         return true;
     }
+
+    public static  function checkArrayElementSubstringOfString(string $string, array $array): bool
+    {
+        if (empty($string) || empty($array)) {
+            return false;
+        }
+
+        foreach ($array as $substr) {
+            if (strpos($string, $substr) !== false) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+
 }

+ 13 - 10
common/libs/IpFilter.php

@@ -16,18 +16,18 @@ class IpFilter
      * @throws BadRequestHttpException
      * @return bool
      */
-    public function frontApiCheck($isLogin = false)
+    public function checkIp($source, $isLogin = false): bool
     {
         $request = Yii::$app->request;
         $getParams = Yii::$app->request->get();
         $postParams = Yii::$app->request->post();
-        $remoteAddr = $_SERVER['REMOTE_ADDR']; // 获取用户 IP 地址
+        $ip = $_SERVER['REMOTE_ADDR']; // 获取用户 IP 地址
 
         //如果IP不在指定范围内
-        if (!self::remoteAddrCall($remoteAddr)) {
+        if (!self::checkIpInAllowRange($ip)) {
             $logPreix = $isLogin ? 'nc_ip_filter_login' : 'nc_ip_filter_other';
-            $getLog = $logPreix . (is_array($getParams) ? json_encode($getParams) : $getParams);
-            $postLog = $logPreix . (is_array($postParams) ? json_encode($postParams) : $postParams);
+            $getLog = sprintf('%s_%s: remote_ip%s: url(%s): param%s', $source, $logPreix, $ip, $request->getAbsoluteUrl(), (is_array($getParams) ? json_encode($getParams) : $getParams));
+            $postLog = sprintf('%s_%s: remote_ip%s: url(%s): param%s', $source, $logPreix, $ip, $request->getAbsoluteUrl(), (is_array($postParams) ? json_encode($postParams) : $postParams));
             LoggerTool::error($getLog);
             LoggerTool::error($postLog);
             return false;
@@ -41,22 +41,25 @@ class IpFilter
      * @throws AddressNotFoundException
      * @throws InvalidDatabaseException
      */
-    public static function remoteAddrCall($remoteAddr): bool
+    public static function checkIpInAllowRange($ip): bool
     {
         // 是否有效的IP
-        if (!filter_var($remoteAddr, FILTER_VALIDATE_IP)) {
+        if (!filter_var($ip, FILTER_VALIDATE_IP)) {
             return false;
         }
-
+        // 开放跳板机IP,后台快速登录使用
+        if (in_array($ip, ['8.219.172.88'])) {
+            return true;
+        }
         // 替换为 GeoLite2 数据库文件的实际路径
         $dbPath = \Yii::getAlias('@common/runtime/geoLite//GeoLite2-Country.mmdb');
         // 初始化 MaxMind 数据库读取器
         $reader = new \GeoIp2\Database\Reader($dbPath);
         // 查询 IP 地址的地理位置
-        $record = $reader->country($remoteAddr);
+        $record = $reader->country($ip);
         // 返回国家名称
         $countryName = $record->country->name;
-        if (!in_array($countryName, ['China'])) {
+        if (!in_array($countryName, ['China', 'Malaysia'])) {
             return false;
         }
 

+ 122 - 2
common/models/forms/TransferForm.php

@@ -2,6 +2,7 @@
 
 namespace common\models\forms;
 
+use common\helpers\bonus\CalcCache;
 use common\helpers\Cache;
 use common\helpers\Date;
 use common\components\Model;
@@ -27,6 +28,7 @@ use common\models\UserBonus;
 use common\models\UserInfo;
 use common\models\UserRelation;
 use common\models\UserSystem;
+use frontendApi\modules\v1\models\Relation;
 use yii\base\Exception;
 use yii\helpers\Json;
 
@@ -39,6 +41,9 @@ class TransferForm extends Model {
     const BONUS_TO_BONUS = 2;
     const BALANCE_TO_BALANCE = 3;
     const POINT_TO_BALANCE = 4;
+    const LOOP_FINISH = 1;
+    const LOOP_CONTINUE = 2;
+    private $_periodNum = 0;
 
     public $toUserName;
     public $toRealName;
@@ -104,6 +109,7 @@ class TransferForm extends Model {
      * @param $attribute
      * @return null
      * @throws Exception
+     * @throws \Exception
      */
     public function initUser($attribute) {
         // 转账记录
@@ -137,8 +143,8 @@ class TransferForm extends Model {
 
 
         // 转账条件判断
-        $orderAmount = Order::find()->where('USER_ID=:USER_ID', [':USER_ID' => $fromUserId])->SUM('ORDER_AMOUNT');
-        $recNum = intval(DecOrder::find()->where('REC_USER_ID=:REC_USER_ID', [':REC_USER_ID' => $fromUserId])->count());
+//        $orderAmount = Order::find()->where('USER_ID=:USER_ID', [':USER_ID' => $fromUserId])->SUM('ORDER_AMOUNT');
+//        $recNum = intval(DecOrder::find()->where('REC_USER_ID=:REC_USER_ID', [':REC_USER_ID' => $fromUserId])->count());
         //$recNum = UserRelation::firstFloorChildNum($fromUserId);
 //        if ($orderAmount < 300 && $recNum==0) {
 //            $this->addError($attribute, '消费未满300元或未推荐新人,暂不能转账');
@@ -179,6 +185,120 @@ class TransferForm extends Model {
                 return null;
             }
         }*/
+
+        // 转账网络限制
+        // 期数
+        $this->_periodNum = Period::instance()->getNowPeriodNum();
+
+        // 发起人
+        $initiator = Info::getBaseUserByUserName($fromUser['USER_NAME']);
+        // 发起人是否报单中心
+        $initiatorIsDec = $initiator['IS_DEC'] == 1;
+
+        // 接受者
+        $recipient = Info::getBaseUserByUserName($this->toUserName);
+        // 发起人是否报单中心
+        $recipientIsDec = $recipient['IS_DEC'] == 1;
+
+        // 如果发起人和接受者都是报单中心,则转账无限制
+        if (($initiatorIsDec == 1) && ($recipientIsDec == 1)) {
+            return true;
+        } else {
+            // 接受人不是报单中心,则只能转给自己推荐网的某个节点
+            $sqlUp =<<<SQL
+                WITH recursive t_rec AS 
+                (
+                    SELECT
+                        m.USER_ID,
+                        U.USER_NAME,
+                        m.PARENT_UID,
+                        U2.USER_NAME AS PARENT_NAME,
+                        1 AS node_level 
+                    FROM
+                        AR_USER_RELATION_NEW m 
+                        LEFT JOIN AR_USER U ON U.ID = m.USER_ID
+                        LEFT JOIN AR_USER U2 ON U2.ID = m.PARENT_UID
+                    WHERE
+                        U.USER_NAME = :USER_NAME 
+                        
+                    UNION ALL
+                    
+                    SELECT
+                        t1.USER_ID,
+                        U.USER_NAME,
+                        t1.PARENT_UID,
+                        U2.USER_NAME AS PARENT_NAME,
+                        t2.node_level + 1 -- 结点层级
+                        
+                    FROM
+                        AR_USER_RELATION_NEW t1
+                        JOIN t_rec t2 ON t2.PARENT_UID = t1.USER_ID
+                        LEFT JOIN AR_USER U ON U.ID = t1.USER_ID
+                        LEFT JOIN AR_USER U2 ON U2.ID = t1.PARENT_UID
+                    ) 
+                SELECT
+                    * 
+                FROM
+                    t_rec
+SQL;
+            // 上级
+            $relationNodeUp = \Yii::$app->db->createCommand($sqlUp)
+                ->bindValue(':USER_NAME' , $fromUser['USER_NAME'])
+                ->queryAll();
+
+            $relationNodeUp = array_column($relationNodeUp, 'USER_NAME');
+            unset($relationNodeUp[$fromUser['USER_NAME']]);
+            if (!in_array($this->toUserName, $relationNodeUp)) {
+                // 下级节点
+                $sqlFloor = <<<SQL
+                    WITH recursive t_rec AS 
+                    (
+                        SELECT
+                            m.USER_ID,
+                            U.USER_NAME,
+                            m.PARENT_UID,
+                            U2.USER_NAME AS PARENT_NAME,
+                            1 AS node_level 
+                        FROM
+                            AR_USER_RELATION_NEW m 
+                            LEFT JOIN AR_USER U ON U.ID = m.USER_ID
+                            LEFT JOIN AR_USER U2 ON U2.ID = m.PARENT_UID
+                        WHERE
+                            U.USER_NAME = :USER_NAME 
+                            
+                        UNION ALL
+                        
+                        SELECT
+                            t1.USER_ID,
+                            U.USER_NAME,
+                            t1.PARENT_UID,
+                            U2.USER_NAME AS PARENT_NAME,
+                            t2.node_level + 1 -- 结点层级
+                            
+                        FROM
+                            AR_USER_RELATION_NEW t1
+                            JOIN t_rec t2 ON t2.PARENT_UID = t1.USER_ID
+                            LEFT JOIN AR_USER U ON U.ID = t1.USER_ID
+                            LEFT JOIN AR_USER U2 ON U2.ID = t1.PARENT_UID
+                        ) 
+                    SELECT
+                        * 
+                    FROM
+                        t_rec
+SQL;
+                // 下级
+                $relationNodeFloor = \Yii::$app->db->createCommand($sqlFloor)
+                    ->bindValue(':USER_NAME', $this->toUserName)
+                    ->queryAll();
+                $relationNodeFloor = array_column($relationNodeFloor, 'USER_NAME');
+                if (!in_array($fromUser['USER_NAME'], $relationNodeFloor)) {
+                    $this->addError($attribute, '转账失败:转入会员不是您的上级或下级');
+                    return null;
+                }
+            }
+        }
+
+        return true;
     }
 
     /**

+ 1 - 0
frontendApi/config/urlManagerRules.php

@@ -117,6 +117,7 @@ return [
             'GET network' => 'network',
             'GET network-list' => 'network-list',
             'GET get-period' => 'get-period',
+            'GET scour-relation'  => 'scour-relation',
         ],
     ],
     [

+ 75 - 0
frontendApi/modules/v1/controllers/AtlasController.php

@@ -8,17 +8,25 @@
 
 namespace frontendApi\modules\v1\controllers;
 
+use common\helpers\bonus\CalcCache;
 use common\helpers\Cache;
 use common\helpers\user\Info;
 use common\models\Period;
+use common\models\StorePerfLog;
 use common\models\UserNetwork;
 use common\models\UserNetworkHidden;
+use frontendApi\modules\v1\models\Relation;
 use Yii;
 use common\models\User;
+use yii\web\HttpException;
 
 class AtlasController extends BaseController {
     public $modelClass = User::class;
 
+    const LOOP_FINISH = 1;
+    const LOOP_CONTINUE = 2;
+    private $_periodNum = 0;
+
     public function behaviors() {
         $behaviors = parent::behaviors();
         return $behaviors;
@@ -150,4 +158,71 @@ class AtlasController extends BaseController {
         ]);
     }
 
+    /**
+     * 查询推荐网络的指定节点.
+     * @return void
+     * @throws HttpException
+     * @throws \Exception
+     */
+    public function actionScourRelation()
+    {
+        // 发起人
+        $initiator = Yii::$app->request->get('initiator');
+        // 接受者
+        $recipient = Yii::$app->request->get('recipient');
+        // 期数
+        $this->_periodNum = Period::instance()->getNowPeriodNum();
+
+        // 发起人
+        $initiatorInfo = Info::getBaseUserByUserName($initiator);
+        if (!$initiatorInfo) {
+            return static::notice('会员不存在', 400);
+        }
+        // 发起人是否报单中心
+        $initiatorIsDec = $initiatorInfo['IS_DEC'] == 1;
+        $initiatorId = $initiatorInfo['ID'];
+
+        // 接受者
+        $recipientInfo = Info::getBaseUserByUserName($recipient);
+        if (!$recipientInfo) {
+            return static::notice('会员不存在', 400);
+        }
+        // 发起人是否报单中心
+        $recipientIsDec = $recipientInfo['IS_DEC'] == 1;
+        $recipientId = $recipientInfo['ID'];
+
+        // 1. 如果发起人和接受者都是报单中心,则转账无限制
+        if (($initiatorIsDec == 1) && ($recipientIsDec == 1)) {
+            return static::notice(['allowable' => true]);
+        }
+
+        // 2. 如果发起人是普通会员,则只能转给自己推荐网的上级某个节点
+        $relation = new Relation();
+        if (!$initiatorIsDec) {
+            // 判断接受者是否是自己的推荐网上级某个节点
+            $allowable = $relation->loopRelationParentDo($initiatorId, function ($parent) use ($recipientId) {
+                $parentUser = CalcCache::getUserInfo($parent['PARENT_UID'], $this->_periodNum);
+                if ($parentUser['ID'] == $recipientId) {
+                    return self::LOOP_FINISH;
+                }
+
+                unset($parent);
+            });
+
+            return static::notice(['allowable' => $allowable]);
+        }
+
+        // 3. 如果发起人是报单中心或者普通会员, 则可以转账给自己的推荐网下级
+        // 判断接受者是否是自己的推荐网上级某个节点
+        $allowable = $relation->loopRelationParentDo($recipientId, function ($parent) use ($initiatorId) {
+            $parentUser = CalcCache::getUserInfo($parent['PARENT_UID'], $this->_periodNum);
+            if ($parentUser['ID'] == $initiatorId) {
+                return self::LOOP_FINISH;
+            }
+
+            unset($parent);
+        });
+
+        return static::notice(['allowable' => $allowable]);
+    }
 }

+ 19 - 2
frontendApi/modules/v1/controllers/BaseController.php

@@ -11,6 +11,7 @@ namespace frontendApi\modules\v1\controllers;
 use common\components\ActiveRecord;
 use common\helpers\Date;
 use common\helpers\Form;
+use common\helpers\Tool;
 use common\libs\IpFilter;
 use frontendApi\modules\v1\models\User;
 use Yii;
@@ -48,9 +49,25 @@ class BaseController extends \yii\rest\ActiveController {
     public function beforeAction($action) {
         $this->forbiddenQuicklyUser();
 
+        $notFilterApi = [
+            'v1/oauth/login',
+            'v1/site/page-data',
+            'v1/site/config',
+            'v1/oauth/is-login-verify',
+            'v1/site/days-diff',
+            'v1/shop/verify-approach-order',
+            'v1/shop/logistics',
+            'v1/shop/logistics-auto',
+            'v1/shop/i-pay88',
+            'v1/shop/re-query-payment',
+            'v1/shop/upop-webhook',
+            'v1/site/random-id',
+            'v1/site/captcha',
+        ];
+
         $request = Yii::$app->request;
-        if (!in_array($request->getUrl(), ['/v1/oauth/login', '/v1/oauth/is-login-verify', '/v1/site/days-diff'])) {
-            if (!(new IpFilter())->frontApiCheck()) {
+        if (\Yii::$app->redis->get('member_ip_filter') && !Tool::checkArrayElementSubstringOfString($request->getUrl(), $notFilterApi)) {
+            if (!(new IpFilter())->checkIp('member')) {
                 throw new \Exception('用户名或密码错误');
             }
         }

+ 4 - 3
frontendApi/modules/v1/models/LoginForm.php

@@ -59,7 +59,7 @@ class LoginForm extends Model
         if (!$this->hasErrors()) {
             $user = $this->getUser();
             if(!$user){
-                $this->addError($attribute, '用户名错误');
+                $this->addError($attribute, '用户名或密码错误');
             } else {
 
 //                $userInfo = UserInfo::findOneAsArray('USER_ID=:USER_ID', [':USER_ID'=>$user['ID']]);
@@ -124,8 +124,9 @@ class LoginForm extends Model
         $result = false;
         try{
             // 验证IP
-            if (!(new IpFilter())->frontApiCheck(true)) {
-                $this->_updateFailTimes($transaction, '用户名或密码错误!');
+            $loginIp = $_SERVER['REMOTE_ADDR'];
+            if (\Yii::$app->redis->get('member_ip_filter') && !(new IpFilter())->checkIp('member', true)) {
+                $this->_updateFailTimes($transaction, '登陆IP异常,无法登陆. ' . $loginIp);
                 throw new Exception('用户名或密码错误');
             }
             $this->getUser();

+ 41 - 0
frontendApi/modules/v1/models/Relation.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace frontendApi\modules\v1\models;
+
+use common\helpers\Cache;
+
+class Relation extends \common\components\ActiveRecord
+{
+    const LOOP_FINISH = 1;
+    const LOOP_CONTINUE = 2;
+    private $_limit = 5000;
+
+    /**
+     * 循环推荐网络的父级
+     * @param $userId
+     * @param callable $callbackFunc
+     * @param int $offset
+     * @return bool
+     */
+    public function loopRelationParentDo($userId, callable $callbackFunc, int $offset = 0): bool
+    {
+        $allParents = Cache::getAllRelationParents($userId);
+        $allData = array_slice($allParents, $offset, $this->_limit);
+        unset($allParents);
+        if ($allData) {
+            foreach ($allData as $data) {
+                $funcResult = $callbackFunc($data);
+                if ($funcResult === self::LOOP_FINISH) {
+                    return true;
+                } elseif ($funcResult === self::LOOP_CONTINUE) {
+                    continue;
+                }
+                unset($data, $funcResult);
+            }
+
+            unset($allData);
+            return $this->loopRelationParentDo($userId, $callbackFunc, $offset + $this->_limit);
+        }
+        return true;
+    }
+}