adminExtend_model = model('AdminExtend'); $this->adminConfig_model = model('AdminConfig'); $this->authGroupAccess_model = model('AuthGroupAccess'); $this->ophost_model = model('Ophost'); $this->ptoken_model = model('Ptoken'); $this->redis = Redis::instance(); } /** * 1. 接收一个域名{顶级域名 | 二级域名} * 首先验证顶级域名是否被封了 * { 被封,切换顶级域名 | 顶级没有被封,检查二级域名是否被封,被封,切二级域名 } * 2. 查找一个可用的域名{ 异步 } * 3. 将可用域名替换被封掉的域名 { 异步 } * 4. 发送企业微信 { 异步 | } */ // 相关表:ophost | admin_config | ptoken | platform public function index() { $param = $this->request->param(); $admin_id = $param['admin_id'] ?? 0; $domain = $param['domain'] ? urldecode($param['domain']) : ''; $this->pt = $param['pt'] ?? ''; Log::info('[ ChangeDomain ] [doAsynRequest] START'); Log::info('[ ChangeDomain ] [Params] '. json_encode($param)); $adminInfo = $this->_getAdminInfo($admin_id); Log::info('[ ChangeDomain ] [adminInfo] '. json_encode($adminInfo)); $this->domain_suffix = $this->getDomainSuffix($domain); if(empty($domain)){ echo json_encode(['msg' => '参数domain不能为空', 'status' => self::PARAM_ERROR]); exit(); } if (!$adminInfo) { // admin_id = 0 && 仅传入了域名的情况 $this->checkTopDomain($domain, $admin_id); Log::info('[ ChangeDomain ] [doAsynRequest] END'); echo json_encode(['msg' => 'Success', 'status' => self::PARAM_SUCCESS]); } else { echo json_encode(['msg' => 'Success', 'status' => self::PARAM_SUCCESS]); fastcgi_finish_request(); /* 响应完成, 关闭连接 */ // 异步处理 $this->asynHandle($admin_id, urlencode($domain)); Log::info('[ ChangeDomain ] [doAsynRequest] END'); } } /** * 异步任务处理 * @param $admin_id * @param $domain */ public function asynHandle($admin_id, $domain) { $domain = urldecode($domain); Log::info('[ ChangeDomain ] [ asynHandle ] start'); Log::info('[ ChangeDomain ] [ asynHandle ] $admin_id:' . $admin_id . '$domain:' . $domain); $this->domain_suffix = $this->getDomainSuffix($domain); Log::info('[ ChangeDomain ] [ asynHandle ] domain_suffix:' . $this->domain_suffix); $adminInfo = $this->_getAdminInfo($admin_id); $platform_list = $adminInfo['platform_list'] ?? ''; $platform_id = $adminInfo['platform_id'] ?? ''; $ophost_id = $adminInfo['ophost_id'] ?? ''; $menu_platform_id = $adminInfo['menu_platform_id'] ?? ''; $menuophost_id = $adminInfo['menuophost_id'] ?? ''; //获取顶级域名 $top_domain = $this->_getTopDomain($domain); //判断顶级域名是否被封 if (!$this->_isValidDomain($top_domain)) { // 企业微信报警,顶级挂了 && 1小时内,同一个顶级不重复报警 $this->checkTopDomain($top_domain, $admin_id); }else{ $valid_ophost_domain_obj = null; $valid_menu_domain_obj = null; Log::info("[ ChangeDomain ] [ asynHandle ] [ platform_list ]" . print_r(json_encode($platform_list), true)); $valid_ophost_domain_obj = $this->getValidHostObj($admin_id, $platform_id, $platform_list); $valid_menu_domain_obj = $this->getValidHostObj($admin_id, $menu_platform_id, $platform_list); if (!$valid_ophost_domain_obj) { $content = ""; $content .= "[Error]".date('Y-m-d H:i:s'); $content .= "\n错误原因:业务域名无域名可切"; $content .= "\n被封域名:{$domain}"; $content .= $this->_packageNotice($admin_id); Log::info("[ ChangeDomain ] [ asynHandle ] [ 业务域名 ]" . date('Y-m-d H:i:s') . "错误原因:无域名可切,被封域名:{$domain}"); $this->_sendWorkChatMessage($content); }else{ // 替换 $this->_modifyOphost($ophost_id, $valid_ophost_domain_obj['ophost_id'], $admin_id); } if (!$valid_menu_domain_obj) { $content = ""; $content .= "[Error]".date('Y-m-d H:i:s'); $content .= "\n错误原因:菜单域名无域名可切"; $content .= "\n被封域名:{$domain}"; $content .= $this->_packageNotice($admin_id); Log::info("[ ChangeDomain ] [ asynHandle ] [ 菜单业务域名 ]" . date('Y-m-d H:i:s') . "错误原因:无域名可切,被封域名:{$domain}"); $this->_sendWorkChatMessage($content); }else{ $this->_modifyMenuOphost($menuophost_id, $valid_menu_domain_obj['ophost_id'], $admin_id); } // 清除缓存 $this->redis->del(self::CACHE_KEY_ADMIN_INFO . $admin_id); } return json(['data'=>'ok']); } public function getDomainSuffix($domain) { $tmp_domain = str_replace('http://', '', $domain); $tmp_domain = str_replace('https://', '', $tmp_domain); $tmp_domain_arr = explode('/', $tmp_domain); unset($tmp_domain_arr[0]); $str = implode('/',$tmp_domain_arr); return $str; } /** * 顶级被封则发送企业微信 && 1小时内,同一个顶级不重复报警 * @param $domain * @param $admin_id */ public function checkTopDomain($domain, $admin_id) { $tmp_domain = $this->filterDomain($domain); if (!$this->redis->get(self::DEAD_DOMAIN_PRE . $tmp_domain)) { // 企业微信报警,顶级挂了 && 1小时内,同一个顶级不重复报警 $content = "顶级域名{$tmp_domain}被封,请尽快处理!"; $content .= $this->_packageNotice($admin_id); $this->_sendWorkChatMessage($content); $this->redis->setex(self::DEAD_DOMAIN_PRE . $tmp_domain, 3600, $tmp_domain); } } /** * 过滤url 中的 http:// https:// * @param $domain * @return mixed */ public function filterDomain($domain) { $tmp_domain = str_replace('http://', '', $domain); $tmp_domain = str_replace('https://', '', $tmp_domain); $tmp_domain_arr = explode('/', $tmp_domain); $tmp_domain = $tmp_domain_arr[0]; return $tmp_domain; } /** * 获取顶级域名 * @param $host * @return string */ private function _getTopDomain($host) { $host = $this->filterDomain($host); Log::info('[ ChangeDomain ] [ TopDomain ] '.print_r($host, true)); preg_match("/^wx\w+/i", $host, $matches); if ($matches) { $arr = explode('.', $host); unset($arr[0]); $top_domain = implode('.', $arr); Log::info('[ ChangeDomain ] [ TopDomain ] [ Result ]' . $top_domain); return $top_domain; } return $host; } /** * 返回可用的域名 * @param $admin_id * @param $platform_id * @param string $platform_list * @return null */ public function getValidHostObj($admin_id, $platform_id, $platform_list = '') { Log::info("[ ChangeDomain ] [ platform_list ] [ start ] " . print_r(json_encode($platform_list), true)); $valid_domain_obj = null; $adminInfo = $this->_getAdminInfo($admin_id); $appid = isset($adminInfo['appid']) ? $adminInfo['appid'] : ''; $platform_id_filter = !empty($platform_list) ? $platform_list : $platform_id; // 查找一个可用的域名 $valid_domain_obj = $this->_getItemDomain($platform_id_filter, $appid); Log::info("[ ChangeDomain ] [ getValidHostObj ] [ 查找到的可用域名信息 ] {$admin_id}" . print_r(json_encode($valid_domain_obj), true)); if (!$valid_domain_obj && !$platform_list) { // 拆解 platform_list $platform_arr = explode(',', $platform_list); $platform_arr = array_diff($platform_arr, [$platform_id]); Log::info("[ ChangeDomain ] [ platform_arr ] " . print_r(json_encode($platform_arr), true)); // 检查平台是否授权,返回授权的平台 foreach ($platform_arr as $key => $value) { $refresh_token = $this->ptoken_model->getToken($value, $admin_id); if ($refresh_token) { // 检查授权平台可用域名 $valid_domain_obj = $this->_getItemDomain($value, $appid); if ($valid_domain_obj) { break; } } } } // 没有可用域名 ,返回NULL return $valid_domain_obj; } /** * 查找一个可用域名 * @param $platform_id * @return null */ private function _getItemDomain($platform_id, $appid = '') { // 查找一个可用的域名 $valid_domain_arr = $this->_getValidDomainList($platform_id); Log::info("[ ChangeDomain ] [ getItemDomain ] [ 可用域名列表 ] " . print_r(json_encode($valid_domain_arr), true)); $valid_domain_obj = null; if ($valid_domain_arr) { foreach ($valid_domain_arr as $k => $item) { // 请求接口,查看域名是否可用? // 如果可用,终止循环,返回可用域名 if ($this->_isValidDomain($appid.'.'.$item['ophost_host'] . '/' . $this->domain_suffix)) { $valid_domain_obj = $item; break; } } } return $valid_domain_obj; } /** * 修改业务域名 * @param $ophost_id * @param $admin_id * @return mixed */ private function _modifyOphost($old_ophost_id, $ophost_id, $admin_id) { $adminInfo = $this->_getAdminInfo($admin_id); $appid = isset($adminInfo['appid']) ? $adminInfo['appid'] : ''; $old_ophost = $this->_getOphostById($old_ophost_id); if (!$this->_isValidDomain($appid . '.' . $old_ophost . '/' . $this->domain_suffix)) { // 修改数据库 $this->adminConfig_model->save(['ophost_id' => $ophost_id], ['admin_id' => $admin_id]); $ophost = $this->_getOphostById($ophost_id); // 发送企业微信 {成功 | 失败 都需要告知} $content = ""; $content .= "[Success]" . date('Y-m-d H:i:s'); $content .= "\n域名类型:业务域名"; $content .= "\n被封域名:{$old_ophost}"; $content .= "\n切换域名:{$ophost}"; Log::info('[ ChangeDomain ] [ modifyOphost ] ' . "[Success]" . date('Y-m-d H:i:s') . "域名类型:业务域名,被封域名:{$old_ophost},切换域名:{$ophost}"); $content .= $this->_packageNotice($admin_id); $this->_sendWorkChatMessage($content); }else{ Log::info('[ ChangeDomain ] [ modifyOphost ] ' . "[Success]" . date('Y-m-d H:i:s') . "业务域名{$old_ophost},未被封无需替换"); } } /** * 修改菜单业务域名 * @param $menuophost_id * @param $admin_id * @return mixed */ private function _modifyMenuOphost($old_ophost_id, $menuophost_id, $admin_id) { $adminInfo = $this->_getAdminInfo($admin_id); $appid = isset($adminInfo['appid']) ? $adminInfo['appid'] : ''; $old_menu_ophost = $this->_getOphostById($old_ophost_id); if (!$this->_isValidDomain($appid . '.' . $old_menu_ophost . '/' . $this->domain_suffix)) { // 修改数据库 $data = $this->adminConfig_model->save(['menuophost_id' => $menuophost_id], ['admin_id' => $admin_id]); if($data){ //刷新菜单业务域名 ChangeMenuService::instance()->setChannelId($admin_id)->setReplaceHostById($menuophost_id)->push(); } $menu_ophost = $this->_getOphostById($menuophost_id); $content = ""; $content .= "[Success]".date('Y-m-d H:i:s'); $content .= "\n域名类型:菜单域名"; $content .= "\n被封域名:{$old_menu_ophost}"; $content .= "\n切换域名:{$menu_ophost}"; Log::info('[ ChangeDomain ] [ modifyMenuOphost ] ' . "[Success]" . date('Y-m-d H:i:s') . "域名类型:菜单域名," . "被封域名:{$old_menu_ophost},切换域名:{$menu_ophost}"); $content .= $this->_packageNotice($admin_id); $this->_sendWorkChatMessage($content); }else{ Log::info('[ ChangeDomain ] [ modifyMenuOphost ] ' . "[Success]" . date('Y-m-d H:i:s') . "菜单业务域名{$old_menu_ophost},未被封无需替换"); } } /** * 获取可用的域名列表 * @param $platform_id * @return mixed */ private function _getValidDomainList($platform_id) { $data = $this->ophost_model->getHostListByPlatformId($platform_id); return $data; } /** * 获取Admin所有信息 * @param $admin_id * @return mixed */ private function _getAdminInfo($admin_id) { return $this->adminConfig_model->getAdminInfoAll($admin_id); } /** * 获取域名信息 * @param $ophost_id * @return string */ private function _getOphostById($ophost_id) { $data = $this->ophost_model->get(['id' => $ophost_id]); return $data ? $data->host : ''; } //发企业微信 private function _sendWorkChatMessage($content) { if (empty($content)) { return false; } try { $wechat = Config::get('wechat'); $wechat['http']['base_uri'] = $wechat['work']['base_uri']; $wechat['http']['timeout'] = 20; $wechat['corp_id'] = $wechat['work']['domain_corp_id']; $wechat['secret'] = $wechat['work']['domain_secret']; $app = Factory::work($wechat); $app['cache'] = new RedisCache(Redis::instanceCache()); $res = $app->message ->message($content) ->ofAgent($wechat['work']['domain_agent_id']) ->toParty($wechat['work']['domain_party_id']) ->send(); Log::info('[ ChangeDomain ] [ sendWorkChatMessage] '.print_r(json_encode($res), true)); return $res; } catch (\Exception $exception) { Log::error('[ ChangeDomain ] [ sendWorkChatMessage] ' . $exception->getMessage()); } } /** * 检测域名是否可以访问,是否被封 * @param $url * @return bool */ private function _isValidDomain($url) { sleep(1); Log::info('[ ChangeDomain ] [ checkurl ] ' . print_r($url, true)); $url = self::VALID_URL_API . "{$url}"; Log::info('[ ChangeDomain ] [ checkurl ] ' . $url); $json_data = Http::get($url); $request_count = 1; if (empty($json_data) && $request_count == 1) { $json_data = Http::get($url); $request_count++; echo $request_count; } Log::info('[ ChangeDomain ] [ checkurl ] [ Json_data] ' . $json_data); if(empty($json_data)){ $this->_sendWorkChatMessage('【Error】域名监控接口返回值为空!!!'); exit(); } $data = json_decode($json_data, true); $ret = is_array($data) ? $data : false; Log::info('[ ChangeDomain ] [ checkurl ] [status] ' . $ret['status']); if (is_array($ret)) { /** * -2.请求失败 * -1.cookie失效 * 0.屏蔽 * 1.正常域名 * 2.白名单 * 3.已跳转到微信110页面 */ if (in_array($ret['status'], [-2, -1, 2])) { $this->_sendWorkChatMessage('【Error】域名监控接口返回值:' . $json_data); exit(); } elseif ($ret['status'] == 1) { Log::info('[ ChangeDomain ] [ checkurl ] [ isValidDomain ] TRUE'); return true; } else { Log::info('[ ChangeDomain ] [ checkurl ] [ isValidDomain ] FALSE A'); return false; } } else { Log::info('[ ChangeDomain ] [ checkurl ] [ isValidDomain ] FALSE B'); return false; } } /** * 返回用户关联信息 * @param $admin_id * @return string */ private function _packageNotice($admin_id) { $html = ''; $group_id = $this->authGroupAccess_model->getGroupId($admin_id); if($group_id == AdminConstants::ADMIN_GROUP_ID_AGENT){ $sql = "SELECT admin.id,admin.username, admin.nickname, admin_extend.create_by, qd_admin.username as qd_username,qd_admin.nickname as qd_nickname ,qd_admin_extend.create_by as qd_create_by ,sw_admin.username as sw_username, sw_admin.nickname as sw_nickname FROM admin LEFT JOIN admin_extend ON admin.id= admin_extend.admin_id LEFT JOIN admin as qd_admin ON admin_extend.create_by = qd_admin.id LEFT JOIN admin_extend as qd_admin_extend ON qd_admin.id = qd_admin_extend.admin_id LEFT JOIN admin as sw_admin on qd_admin_extend.create_by = sw_admin.id WHERE admin.id ={$admin_id} LIMIT 1"; $res = Db::query($sql); if($res){ $data = $res[0]; $html .= "\n代理商ID:{$data['id']} \n代理商账号:{$data['username']} \n代理商昵称:{$data['nickname']}"; $html .= "\n渠道商ID:{$data['create_by']} \n渠道商账号:{$data['qd_username']} \n渠道商昵称:{$data['qd_nickname']}"; $html .= "\n商务ID:{$data['qd_create_by']} \n商务账号:{$data['sw_username']} \n商务昵称:{$data['sw_nickname']}"; } } elseif ($group_id == AdminConstants::ADMIN_GROUP_ID_CHANNEL){ $sql = "SELECT admin.id,admin.username, admin.nickname, admin_extend.create_by, sw_admin.username as sw_username,sw_admin.nickname as sw_nickname FROM admin LEFT JOIN admin_extend ON admin.id= admin_extend.admin_id LEFT JOIN admin as sw_admin ON admin_extend.create_by = sw_admin.id LEFT JOIN admin_extend as sw_admin_extend ON sw_admin.id = sw_admin_extend.admin_id WHERE admin.id ={$admin_id} LIMIT 1"; $res = Db::query($sql); if($res){ $data = $res[0]; $html .= "\n渠道商ID:{$data['id']} \n渠道商账号:{$data['username']} \n渠道商昵称:{$data['nickname']}"; $html .= "\n商务ID:{$data['create_by']} \n商务账号:{$data['sw_username']} \n商务昵称:{$data['sw_nickname']}"; } }elseif ($group_id == AdminConstants::ADMIN_GROUP_ID_ADMIN){ $sql = "SELECT id,username, nickname FROM admin WHERE id ={$admin_id} LIMIT 1"; $res = Db::query($sql); if($res){ $data = $res[0]; $html .= "\n商务ID:{$data['id']} \n商务账号:{$data['username']} \n商务昵称:{$data['nickname']}"; } } $html .= "\n平台:{$this->pt}"; return $html; } }