* @copyright walkor * @link http://www.workerman.net/ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ namespace app\worker; /** * 推送主逻辑 * 主要是处理 onMessage onClose */ use GatewayWorker\Lib\Gateway; use app\worker\Application; use think\facade\Config; use Lcobucci\JWT\Builder; use Lcobucci\JWT\Parser; use thans\jwt\provider\JWT\Lcobucci; use utils\Aes; use app\enterprise\model\User; class Events { // 使用TP框架 public static function onWorkerStart() { $app = new Application; $app->initialize(); } // 当有客户端连接时,将client_id返回,让mvc框架判断当前uid并执行绑定 public static function onConnect($client_id) { Gateway::sendToClient($client_id, json_encode(array( 'type' => 'init', 'client_id' => $client_id, ))); self::onlineStatistics(); } /** * 有消息时 * @param int $client_id * @param mixed $message */ public static function onMessage($client_id, $message) { // 客户端传递的是json数据 $message_data = json_decode($message, true); if(!$message_data) { return ; } // 根据类型执行不同的业务 switch($message_data['type']) { // 客户端回应服务端的心跳 case 'pong': // 三次心跳后如果没有登录则断开连接 $_SESSION['pong_times'] = ($_SESSION['pong_times'] ?? 0) + 1; $user_id=$_SESSION['user_id'] ?? ''; $isQrLogin = $message_data['isQrLogin'] ?? 0; // 二维码登录逻辑,每60秒 if($isQrLogin){ // 生成新的二维码链接 $domain=config('app.app_host'); $appid=config('app.app_id'); $token=urlencode(authcode($client_id,'ENCODE',$appid,100)); $qrurl=rtrim($domain,'/') . '/scan/a/' . $token; Gateway::sendToClient($client_id, json_encode(array( 'type' => 'codeLoginQr', 'qrurl' => $qrurl, ))); } // 如果是扫码登录则不做断开处理 if(!$user_id && $_SESSION['pong_times'] >= 3 && !$isQrLogin){ $_SESSION['pong_times'] = 0; self::closeClient($client_id); } break; case 'ping': self::sendStatus($client_id); break; case 'bindUid': self::auth($client_id,$message_data); break; } return; } protected static function sendStatus($client_id){ $uid=$_SESSION['user_id'] ?? 0; $multiport=false; if($uid){ $arr=Gateway::getClientIdByUid($uid); if(count($arr)>1){ $multiport=true; } } Gateway::sendToClient($client_id, json_encode(array( 'type' => 'pong', 'multiport' => $multiport, ))); } //验证用户的真实性并绑定 protected static function auth($client_id, $msg){ $token=$msg['token'] ?? ''; $config = Config::get('jwt'); $keys = $config['secret'] ?: [ 'public' => $config['public_key'], 'private' => $config['private_key'], 'password' => $config['password'], ]; $provider = new Lcobucci(new Builder(), new Parser(), $config['algo'], $keys); try { $token=str_replace('bearer ','',$token); $jwtData = $provider->decode((string)$token); } catch (\Exception $exception) { self::closeClient($client_id); } $userInfo = $jwtData['info']->getValue(); //解密token中的用户信息 $userInfo = Aes::decrypt($userInfo, config('app.aes_token_key')); //解析json $userInfo = (array)json_decode($userInfo, true); if(!$userInfo){ self::closeClient($client_id); } $_SESSION['user_id']=$userInfo['user_id']; $_SESSION['role']=$userInfo['role']; if($_SESSION['role'] != 0){ $user_id = $userInfo['user_id']; $is_online = User::isOnline($user_id); if ($is_online == 0) { //更新客服上线状态 User::setOnline($user_id, 1); Gateway::sendToClient($client_id, json_encode(array( 'type' => 'sign', 'data' => ['is_sign' => 1], ))); } } self::sendStatus($client_id); } //断开连接 protected static function closeClient($client_id){ self::onlineStatistics(); $_SESSION['user_id']=null; Gateway::closeClient($client_id); } /** * 当断开连接时 * @param int $client_id */ public static function onClose($client_id) { $user_id=$_SESSION['user_id'] ?? ''; if($user_id){ Gateway::sendToAll(json_encode(array( 'type' => 'isOnline', 'time' => time(), 'data' => ['id'=>$user_id,'is_online'=>0] ))); if($_SESSION['role'] != 0){ //更新客服离线状态 User::setOnline($user_id, 0); } } self::onlineStatistics(); } public static function onlineStatistics() { // 通知后台在线用户数和在线设备数 $data=[ 'type' => 'statistics', 'time' => time(), 'data' => [ 'onlineCount'=>Gateway::getAllUidCount() ?? 0, 'clientCount'=>Gateway::getAllClientCount() ?? 0, ] ]; Gateway::sendToGroup('admin-manage', json_encode($data)); Gateway::sendToUid(1, json_encode($data)); } }