WebSocketService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <?php
  2. namespace App\Services;
  3. use App\Model\DBCache;
  4. use App\Module\Base;
  5. use App\Module\Chat;
  6. use App\Module\Users;
  7. use Cache;
  8. use DB;
  9. use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface;
  10. use Swoole\Http\Request;
  11. use Swoole\WebSocket\Frame;
  12. use Swoole\WebSocket\Server;
  13. /**
  14. * @see https://wiki.swoole.com/#/start/start_ws_server
  15. */
  16. class WebSocketService implements WebSocketHandlerInterface
  17. {
  18. /**
  19. * 声明没有参数的构造函数
  20. * WebSocketService constructor.
  21. */
  22. public function __construct()
  23. {
  24. }
  25. /**
  26. * 连接建立时触发
  27. * @param Server $server
  28. * @param Request $request
  29. */
  30. public function onOpen(Server $server, Request $request)
  31. {
  32. global $_A;
  33. $_A = [
  34. '__static_langdata' => [],
  35. ];
  36. //
  37. $to = $request->fd;
  38. if (!isset($request->get['token'])) {
  39. $server->push($to, $this->formatMsgSend([
  40. 'messageType' => 'error',
  41. 'type' => 'user',
  42. 'content' => [
  43. 'error' => '参数错误'
  44. ],
  45. ]));
  46. $server->close($to);
  47. $this->forgetUser($to);
  48. return;
  49. }
  50. //
  51. $token = $request->get['token'];
  52. $cacheKey = "ws::token:" . md5($token);
  53. $username = Cache::remember($cacheKey, now()->addSeconds(1), function () use ($token) {
  54. list($id, $username, $encrypt, $timestamp) = explode("@", base64_decode($token) . "@@@@");
  55. if (intval($id) > 0 && intval($timestamp) + 2592000 > time()) {
  56. if (DB::table('users')->where(['id' => $id, 'username' => $username, 'encrypt' => $encrypt])->exists()) {
  57. return $username;
  58. }
  59. }
  60. return null;
  61. });
  62. if (empty($username)) {
  63. Cache::forget($cacheKey);
  64. $server->push($to, $this->formatMsgSend([
  65. 'messageType' => 'error',
  66. 'type' => 'user',
  67. 'content' => [
  68. 'error' => '会员不存在',
  69. ],
  70. ]));
  71. $server->close($to);
  72. $this->forgetUser($to);
  73. return;
  74. }
  75. //
  76. $wsid = $this->name2fd($username);
  77. if ($wsid > 0) {
  78. $server->push($wsid, $this->formatMsgSend([
  79. 'messageType' => 'forced',
  80. 'type' => 'user',
  81. 'content' => [
  82. 'ip' => Base::getIp(),
  83. 'time' => time(),
  84. 'new_wsid' => $to,
  85. 'old_wsid' => $wsid,
  86. ],
  87. ]));
  88. }
  89. $this->saveUser($to, $username);
  90. $server->push($to, $this->formatMsgSend([
  91. 'messageType' => 'open',
  92. 'type' => 'user',
  93. 'content' => [
  94. 'swid' => $to,
  95. ],
  96. ]));
  97. //
  98. $lastMsg = Base::DBC2A(DB::table('chat_msg')->where('receive', $username)->orderByDesc('indate')->first());
  99. if ($lastMsg && $lastMsg['roger'] === 0) {
  100. $content = Base::string2array($lastMsg['message']);
  101. $content['resend'] = 1;
  102. $content['id'] = $lastMsg['id'];
  103. $content['username'] = $lastMsg['username'];
  104. $content['userimg'] = Users::userimg($lastMsg['username']);
  105. $content['indate'] = $lastMsg['indate'];
  106. $server->push($to, $this->formatMsgSend([
  107. 'messageType' => 'send',
  108. 'contentId' => $lastMsg['id'],
  109. 'type' => 'user',
  110. 'sender' => $lastMsg['username'],
  111. 'target' => $lastMsg['receive'],
  112. 'content' => $content,
  113. 'time' => $lastMsg['indate'],
  114. ]));
  115. }
  116. }
  117. /**
  118. * 收到消息时触发
  119. * @param Server $server
  120. * @param Frame $frame
  121. */
  122. public function onMessage(Server $server, Frame $frame)
  123. {
  124. global $_A;
  125. $_A = [
  126. '__static_langdata' => [],
  127. ];
  128. //
  129. $data = $this->formatMsgReceive($frame->data);
  130. $feedback = [
  131. 'status' => 1,
  132. 'message' => '',
  133. ];
  134. switch ($data['type']) {
  135. /**
  136. * 刷新
  137. */
  138. case 'refresh':
  139. DB::table('users')->where('username', $this->fd2name($frame->fd))->update(['wsdate' => time()]);
  140. break;
  141. /**
  142. * 未读消息总数
  143. */
  144. case 'unread':
  145. $from = $this->fd2name($frame->fd);
  146. if ($from) {
  147. $num = intval(DB::table('chat_dialog')->where('user1', $from)->sum('unread1'));
  148. $num+= intval(DB::table('chat_dialog')->where('user2', $from)->sum('unread2'));
  149. $feedback['message'] = $num;
  150. } else {
  151. $feedback['message'] = 0;
  152. }
  153. break;
  154. /**
  155. * 已读会员消息
  156. */
  157. case 'read':
  158. $to = $this->name2fd($data['target']);
  159. if ($to) {
  160. $dialog = Chat::openDialog($this->fd2name($frame->fd), $data['target']);
  161. if (!Base::isError($dialog)) {
  162. $dialog = $dialog['data'];
  163. $upArray = [];
  164. if ($dialog['user1'] == $dialog['user2']) {
  165. $upArray['unread1'] = 0;
  166. $upArray['unread2'] = 0;
  167. } else {
  168. $upArray[($dialog['recField'] == 1 ? 'unread2' : 'unread1')] = 0;
  169. }
  170. DB::table('chat_dialog')->where('id', $dialog['id'])->update($upArray);
  171. }
  172. }
  173. break;
  174. /**
  175. * 收到信息回执
  176. */
  177. case 'roger':
  178. if ($data['contentId'] > 0) {
  179. DB::table('chat_msg')->where([
  180. 'id' => $data['contentId'],
  181. 'receive' => $this->fd2name($frame->fd),
  182. ])->update([
  183. 'roger' => 1,
  184. ]);
  185. }
  186. break;
  187. /**
  188. * 发给用户
  189. */
  190. case 'user':
  191. $to = $this->name2fd($data['target']);
  192. $res = Chat::saveMessage($this->fd2name($frame->fd), $data['target'], $data['content']);
  193. if (Base::isError($res)) {
  194. $feedback = [
  195. 'status' => 0,
  196. 'message' => $res['msg'],
  197. ];
  198. } else {
  199. $contentId = $res['data']['id'];
  200. $data['contentId'] = $contentId;
  201. $data['content']['id'] = $contentId;
  202. $feedback['message'] = $contentId;
  203. if ($to) {
  204. $server->push($to, $this->formatMsgSend($data));
  205. }
  206. }
  207. break;
  208. /**
  209. * 发给整个团队
  210. */
  211. case 'team':
  212. if (Base::val($data['content'], 'type') === 'taskA') {
  213. $taskId = intval(Base::val($data['content'], 'taskDetail.id'));
  214. if ($taskId > 0) {
  215. $userLists = $this->getTaskUsers($taskId);
  216. } else {
  217. $userLists = $this->getTeamUsers();
  218. }
  219. foreach ($userLists as $user) {
  220. $data['target'] = $user['username'];
  221. $server->push($user['wsid'], $this->formatMsgSend($data));
  222. }
  223. }
  224. break;
  225. }
  226. if ($data['messageId']) {
  227. $server->push($frame->fd, $this->formatMsgSend([
  228. 'messageType' => 'feedback',
  229. 'messageId' => $data['messageId'],
  230. 'type' => 'user',
  231. 'content' => $feedback,
  232. ]));
  233. }
  234. }
  235. /**
  236. * 关闭连接时触发
  237. * @param Server $server
  238. * @param $fd
  239. * @param $reactorId
  240. */
  241. public function onClose(Server $server, $fd, $reactorId)
  242. {
  243. $this->forgetUser($fd);
  244. }
  245. /** ****************************************************************************** */
  246. /** ****************************************************************************** */
  247. /** ****************************************************************************** */
  248. /**
  249. * 格式化信息(来自接收)
  250. * @param $data
  251. * @return array
  252. */
  253. private function formatMsgReceive($data) {
  254. return $this->formatMsgData(Base::json2array($data));
  255. }
  256. /**
  257. * 格式化信息(用于发送)
  258. * @param $array
  259. * @return string
  260. */
  261. private function formatMsgSend($array) {
  262. return Base::array2json($this->formatMsgData($array));
  263. }
  264. /**
  265. * 格式化信息
  266. * @param array $array
  267. * @return array
  268. */
  269. private function formatMsgData($array = []) {
  270. if (!is_array($array)) {
  271. $array = [];
  272. }
  273. if (!isset($array['messageType'])) $array['messageType'] = '';
  274. if (!isset($array['messageId'])) $array['messageId'] = '';
  275. if (!isset($array['contentId'])) $array['contentId'] = 0;
  276. if (!isset($array['type'])) $array['type'] = '';
  277. if (!isset($array['sender'])) $array['sender'] = null;
  278. if (!isset($array['target'])) $array['target'] = null;
  279. if (!isset($array['content'])) $array['content'] = [];
  280. if (!isset($array['time'])) $array['time'] = time();
  281. if (!is_array($array['content'])) $array['content'] = [];
  282. $array['contentId'] = intval($array['contentId']);
  283. return $array;
  284. }
  285. /**
  286. * 保存用户wsid
  287. * @param $fd
  288. * @param $username
  289. */
  290. private function saveUser($fd, $username)
  291. {
  292. $this->forgetUser($fd);
  293. $this->forgetName($username);
  294. DB::table('users')->where('username', $username)->update(['wsid' => $fd, 'wsdate' => time()]);
  295. }
  296. /**
  297. * 清除用户wsid
  298. * @param $fd
  299. */
  300. private function forgetUser($fd)
  301. {
  302. $this->forgetFd($fd);
  303. DB::table('users')->where('wsid', $fd)->update(['wsid' => 0]);
  304. }
  305. /**
  306. * 根据wsid清除缓存
  307. * @param $fd
  308. */
  309. private function forgetFd($fd) {
  310. Cache::forget('ws::name:' . $this->fd2name($fd));
  311. Cache::forget('ws::fd:' . $fd);
  312. }
  313. /**
  314. * 根据username清除缓存
  315. * @param $username
  316. */
  317. private function forgetName($username) {
  318. Cache::forget('ws::fd:' . $this->name2fd($username));
  319. Cache::forget('ws::name:' . $username);
  320. }
  321. /**
  322. * 获取团队用户
  323. * @return array|string
  324. */
  325. private function getTeamUsers()
  326. {
  327. return Base::DBC2A(DB::table('users')->select(['wsid', 'username'])->where([
  328. ['wsid', '>', 0],
  329. ['wsdate', '>', time() - 600],
  330. ])->get());
  331. }
  332. /**
  333. * 获取跟任务有关系的用户(关注的、在项目里的、负责人、创建者)
  334. * @param $taskId
  335. * @return array
  336. */
  337. private function getTaskUsers($taskId)
  338. {
  339. $taskDeatil = Base::DBC2A(DB::table('project_task')->select(['follower', 'createuser', 'username', 'projectid'])->where('id', $taskId)->first());
  340. if (empty($taskDeatil)) {
  341. return [];
  342. }
  343. //关注的用户
  344. $userArray = Base::string2array($taskDeatil['follower']);
  345. //创建者
  346. $userArray[] = $taskDeatil['createuser'];
  347. //负责人
  348. $userArray[] = $taskDeatil['username'];
  349. //在项目里的用户
  350. if ($taskDeatil['projectid'] > 0) {
  351. $tempLists = Base::DBC2A(DB::table('project_users')->select(['username'])->where(['projectid' => $taskDeatil['projectid'], 'type' => '成员' ])->get());
  352. foreach ($tempLists AS $item) {
  353. $userArray[] = $item['username'];
  354. }
  355. }
  356. //
  357. return Base::DBC2A(DB::table('users')->select(['wsid', 'username'])->where([
  358. ['wsid', '>', 0],
  359. ['wsdate', '>', time() - 600],
  360. ])->whereIn('username', array_values(array_unique($userArray)))->get());
  361. }
  362. /**
  363. * wsid获取用户名
  364. * @param $fd
  365. * @return mixed
  366. */
  367. private function fd2name($fd)
  368. {
  369. return DBCache::table('users')->cacheKeyname('ws::fd:' . $fd)->select(['username'])->where('wsid', $fd)->value('username');
  370. }
  371. /**
  372. * 用户名获取wsid
  373. * @param $username
  374. * @return mixed
  375. */
  376. private function name2fd($username)
  377. {
  378. return DBCache::table('users')->cacheKeyname('ws::name:' . $username)->select(['wsid'])->where('username', $username)->value('wsid');
  379. }
  380. }