Events.php 6.2 KB

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