Events.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <?php
  2. /**
  3. * This file is part of workerman.
  4. *
  5. * Licensed under The MIT License
  6. * For full copyright and license information, please see the MIT-LICENSE.txt
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @author walkor<walkor@workerman.net>
  10. * @copyright walkor<walkor@workerman.net>
  11. * @link http://www.workerman.net/
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace app\worker;
  15. /**
  16. * 推送主逻辑
  17. * 主要是处理 onMessage onClose
  18. */
  19. use GatewayWorker\Lib\Gateway;
  20. use app\worker\Application;
  21. use think\facade\Config;
  22. use Lcobucci\JWT\Builder;
  23. use Lcobucci\JWT\Parser;
  24. use thans\jwt\provider\JWT\Lcobucci;
  25. use utils\Aes;
  26. use app\admin\model\Admin;
  27. use think\facade\Log;
  28. class Events
  29. {
  30. // 使用TP框架
  31. public static function onWorkerStart()
  32. {
  33. $app = new Application;
  34. $app->initialize();
  35. }
  36. // 当有客户端连接时,将client_id返回,让mvc框架判断当前uid并执行绑定
  37. public static function onConnect($client_id)
  38. {
  39. Gateway::sendToClient($client_id, json_encode(array(
  40. 'type' => 'init',
  41. 'client_id' => $client_id,
  42. )));
  43. self::onlineStatistics();
  44. }
  45. /**
  46. * 有消息时
  47. * @param int $client_id
  48. * @param mixed $message
  49. */
  50. public static function onMessage($client_id, $message)
  51. {
  52. // 客户端传递的是json数据
  53. $message_data = json_decode($message, true);
  54. if(!$message_data)
  55. {
  56. return ;
  57. }
  58. // 根据类型执行不同的业务
  59. switch($message_data['type'])
  60. {
  61. // 客户端回应服务端的心跳
  62. case 'pong':
  63. // 三次心跳后如果没有登录则断开连接
  64. $_SESSION['pong_times'] = ($_SESSION['pong_times'] ?? 0) + 1;
  65. $user_id=$_SESSION['user_id'] ?? '';
  66. $isQrLogin = $message_data['isQrLogin'] ?? 0;
  67. // 二维码登录逻辑,每60秒
  68. if($isQrLogin){
  69. // 生成新的二维码链接
  70. $domain=config('app.app_host');
  71. $appid=config('app.app_id');
  72. $token=urlencode(authcode($client_id,'ENCODE',$appid,100));
  73. $qrurl=rtrim($domain,'/') . '/scan/a/' . $token;
  74. Gateway::sendToClient($client_id, json_encode(array(
  75. 'type' => 'codeLoginQr',
  76. 'qrurl' => $qrurl,
  77. )));
  78. }
  79. // 如果是扫码登录则不做断开处理
  80. if(!$user_id && $_SESSION['pong_times'] >= 3 && !$isQrLogin){
  81. $_SESSION['pong_times'] = 0;
  82. self::closeClient($client_id);
  83. }
  84. break;
  85. case 'ping':
  86. self::sendStatus($client_id);
  87. break;
  88. case 'bindUid':
  89. self::auth($client_id,$message_data);
  90. break;
  91. }
  92. return;
  93. }
  94. protected static function sendStatus($client_id){
  95. $uid=$_SESSION['user_id'] ?? 0;
  96. $multiport=false;
  97. if($uid){
  98. $arr=Gateway::getClientIdByUid($uid);
  99. if(count($arr)>1){
  100. $multiport=true;
  101. }
  102. }
  103. Gateway::sendToClient($client_id, json_encode(array(
  104. 'type' => 'pong',
  105. 'multiport' => $multiport,
  106. )));
  107. }
  108. //验证用户的真实性并绑定
  109. protected static function auth($client_id, $msg){
  110. $token=$msg['token'] ?? '';
  111. $config = Config::get('jwt');
  112. $keys = $config['secret'] ?: [
  113. 'public' => $config['public_key'],
  114. 'private' => $config['private_key'],
  115. 'password' => $config['password'],
  116. ];
  117. $provider = new Lcobucci(new Builder(), new Parser(), $config['algo'], $keys);
  118. try {
  119. $token=str_replace('bearer ','',$token);
  120. $jwtData = $provider->decode((string)$token);
  121. } catch (\Exception $exception) {
  122. self::closeClient($client_id);
  123. }
  124. $userInfo = $jwtData['info']->getValue();
  125. //解密token中的用户信息
  126. $userInfo = Aes::decrypt($userInfo, config('app.aes_token_key'));
  127. //解析json
  128. $userInfo = (array)json_decode($userInfo, true);
  129. if(!$userInfo){
  130. self::closeClient($client_id);
  131. }
  132. $_SESSION['user_id']=$userInfo['user_id'];
  133. $_SESSION['role']=$userInfo['role'];
  134. if($_SESSION['role'] != 0){
  135. $admin_id = $_SESSION['user_id'];
  136. $is_online = Admin::isOnline($admin_id);
  137. if ($is_online == 0) {
  138. //更新客服上线状态
  139. Admin::setOnline($admin_id, 1);
  140. Gateway::sendToClient($client_id, json_encode(array(
  141. 'type' => 'sign',
  142. 'data' => ['is_sign' => 1],
  143. )));
  144. }
  145. }
  146. self::sendStatus($client_id);
  147. }
  148. //断开连接
  149. protected static function closeClient($client_id){
  150. self::onlineStatistics();
  151. $_SESSION['user_id']=null;
  152. Gateway::closeClient($client_id);
  153. }
  154. /**
  155. * 当断开连接时
  156. * @param int $client_id
  157. */
  158. public static function onClose($client_id)
  159. {
  160. $user_id=$_SESSION['user_id'] ?? '';
  161. if($user_id){
  162. Gateway::sendToAll(json_encode(array(
  163. 'type' => 'isOnline',
  164. 'time' => time(),
  165. 'data' => ['id'=>$user_id,'is_online'=>0]
  166. )));
  167. if($_SESSION['role'] != 0){
  168. //更新客服离线状态
  169. Admin::setOnline($user_id, 0);
  170. }
  171. }
  172. self::onlineStatistics();
  173. }
  174. public static function onlineStatistics()
  175. {
  176. // 通知后台在线用户数和在线设备数
  177. $data=[
  178. 'type' => 'statistics',
  179. 'time' => time(),
  180. 'data' => [
  181. 'onlineCount'=>Gateway::getAllUidCount() ?? 0,
  182. 'clientCount'=>Gateway::getAllClientCount() ?? 0,
  183. ]
  184. ];
  185. Gateway::sendToGroup('admin-manage', json_encode($data));
  186. Gateway::sendToUid(1, json_encode($data));
  187. }
  188. }