Events.php 6.5 KB

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