CustomerService.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. <?php
  2. namespace addons\shopro\library\chat\provider\auth\traits;
  3. use addons\shopro\exception\ShoproException;
  4. use addons\shopro\library\chat\traits\DebugEvent;
  5. use addons\shopro\library\chat\traits\Helper;
  6. use addons\shopro\library\chat\traits\Session;
  7. /**
  8. * 客服事件
  9. */
  10. trait CustomerService
  11. {
  12. /**
  13. * debug 方式注册事件
  14. */
  15. use DebugEvent;
  16. /**
  17. * 助手方法
  18. */
  19. use Helper;
  20. /**
  21. * session 存储助手
  22. */
  23. use Session;
  24. public function customerServiceOn()
  25. {
  26. // 客服登录
  27. $this->register('customer_service_login', function ($data, $callback) {
  28. // 加入的客服房间
  29. $room_id = $data['room_id'] ?? 'admin';
  30. $this->session('room_id', $room_id);
  31. // 存储当前房间
  32. $this->nspRoomIdAdd($room_id);
  33. // 当前管理员信息
  34. $auth = $this->session('auth');
  35. $admin = $this->session('auth_user');
  36. if (!$this->chatService->customerServiceLogin($room_id, $admin['id'], $auth)) {
  37. throw new ShoproException('客服登录失败');
  38. }
  39. // 注册链接上客服事件
  40. $this->customerServiceEvent();
  41. // 客服上线
  42. $this->customerServiceOnline();
  43. // 获取客服常用语
  44. $commonWords = $this->getter('db')->getChatCommonWords($room_id);
  45. // 通知自己登录成功
  46. $this->sender->successSocket($callback, '客服登录成功', [
  47. 'customer_service' => $this->session('customer_service'),
  48. 'common_words' => $commonWords
  49. ]);
  50. });
  51. }
  52. /**
  53. * 客服的所有事件
  54. *
  55. * @return void
  56. */
  57. public function customerServiceEvent()
  58. {
  59. // 检测是否登录过
  60. if ($this->socket->listeners('customer_service_init')) {
  61. // 已经注册过了,避免重复注册
  62. return false;
  63. }
  64. // 客服登录之后的所有事件
  65. // 客服初始化
  66. $this->register('customer_service_init', function ($data, $callback) {
  67. $room_id = $this->session('room_id');
  68. $customerService = $this->session('customer_service');
  69. // 获取当前客服正在服务的顾客
  70. $chatUsers = $this->getter()->getCustomersIngFormatByCustomerService($room_id, $customerService['id']);
  71. $ingChatUserIds = array_column($chatUsers, 'id');
  72. // 获取等待中的顾客
  73. $waitings = $this->getter()->getCustomersFormatWaiting($room_id);
  74. // 获取客服历史服务过的顾客
  75. $histories = $this->getter()->getCustomersHistoryFormatByCustomerService($room_id, $customerService['id'], $ingChatUserIds);
  76. $this->sender->successSocket($callback, '初始化成功', [
  77. 'onlines' => $chatUsers,
  78. 'histories' => $histories,
  79. 'waitings' => $waitings,
  80. ]);
  81. });
  82. // 发送消息
  83. $this->register('message', function ($data, $callback) {
  84. $session_id = $data['session_id'] ?? ''; // 接收者
  85. $message = $data['message'] ?? []; // 发送的消息
  86. $customerService = $this->session('customer_service');
  87. // 给用户发送消息
  88. $this->sender->messageToCustomer($message, [
  89. 'sender_identify' => 'customer_service',
  90. 'customer_service' => $customerService
  91. ], $session_id);
  92. // 通知自己发送成功
  93. $this->sender->successSocket($callback, '发送成功');
  94. });
  95. // 获取消息列表
  96. $this->register('messages', function ($data, $callback) {
  97. // 当前房间
  98. $room_id = $this->session('room_id');
  99. $session_id = $data['session_id']; // 要获取的顾客
  100. // 获取 顾客 聊天记录
  101. $messages = $this->getter('db')->getCustomerMessagesBySessionId($room_id, $session_id, 'customer', $data);
  102. // 获取消息列表
  103. $this->sender->successSocket($callback, '获取成功', [
  104. 'messages' => $messages
  105. ]);
  106. });
  107. // 接入用户
  108. $this->register('access', function ($data, $callback) {
  109. $session_id = $data['session_id']; // 要接入的顾客
  110. $room_id = $this->session('room_id');
  111. $customerService = $this->session('customer_service');
  112. // 将当前 用户与 客服绑定
  113. $this->chatService->bindCustomerServiceBySessionId($room_id, $session_id, $customerService);
  114. // 将用户 session_id 从等待排名中移除(这个用户的所有客户端都会被接入)
  115. $this->nspWaitingDel($room_id, $session_id);
  116. // 排队发生变化,通知房间中所有排队用户
  117. $this->sender->allWaitingQueue($room_id);
  118. // 顾客被接入,通知所有自己的客户端被接入,通知房间中所有客服用户被接入(等待中移除),通知新客服,新用户接入
  119. $this->sender->customerAccessed($room_id, $session_id, $customerService);
  120. // 获取消息列表
  121. $this->sender->successSocket($callback, '接入成功');
  122. });
  123. // 转接
  124. $this->register('transfer', function ($data, $callback) {
  125. // 要转接的顾客
  126. $session_id = $data['session_id'] ?? 0;
  127. // 要转接的客服 id
  128. $new_customer_service_id = $data['customer_service_id'] ?? 0;
  129. // 当前客服信息
  130. $room_id = $this->session('room_id');
  131. $customerService = $this->session('customer_service');
  132. if (!$new_customer_service_id) {
  133. // 没有传入转接客服 id
  134. throw new ShoproException('请选择要转接的客服');
  135. }
  136. // 不能转接给自己
  137. if ($new_customer_service_id == $customerService['id']) {
  138. // 不能转接给自己
  139. throw new ShoproException('您不能转接给自己');
  140. }
  141. // 获取被转接入的客服, 自动只取客服信息,过滤重复
  142. $newCustomerService = $this->getter('socket')->getCustomerServiceById($room_id, $new_customer_service_id);
  143. if (!$newCustomerService) {
  144. throw new ShoproException('转接的客服不在线');
  145. }
  146. // 转接客户,加入新客服,移除老客服
  147. $this->chatService->transferCustomerServiceBySessionId($room_id, $session_id, $customerService, $newCustomerService);
  148. // 将用户 session_id 从等待排名中移除(这个用户的所有客户端都会被接入)
  149. $this->nspWaitingDel($room_id, $session_id);
  150. // 排队发生变化,通知房间中所有排队用户
  151. $this->sender->allWaitingQueue($room_id);
  152. // 顾客被接入,通知所有自己的客户端被接入,通知房间中所有客服用户被接入(等待中移除),通知新客服,新用户接入
  153. $this->sender->customerTransfer($room_id, $session_id, $customerService, $newCustomerService);
  154. // 通知老客服,转接成功
  155. $this->sender->successSocket($callback, '转接成功');
  156. });
  157. // 断开连接中的顾客
  158. $this->register('break_customer', function ($data, $callback) {
  159. // 当前客服信息
  160. $room_id = $this->session('room_id');
  161. $customerService = $this->session('customer_service');
  162. // 要断开的顾客
  163. $session_id = $data['session_id'];
  164. // 结束并断开客服
  165. $this->chatService->breakCustomerServiceBySessionId($room_id, $session_id, $customerService);
  166. // 服务结束,通知顾客客服断开连接
  167. $this->sender->customerServiceBreak($session_id);
  168. $this->sender->successSocket($callback, '服务结束成功');
  169. });
  170. // 删除历史中的顾客
  171. $this->register('del_customer', function ($data, $callback) {
  172. // 当前客服信息
  173. $room_id = $this->session('room_id');
  174. $customerService = $this->session('customer_service');
  175. // 要删除的顾客
  176. $session_id = $data['session_id'];
  177. $is_del_record = $data['is_del_record'] ?? false; // 是否删除聊天记录
  178. $chatUser = $this->getter('db')->getChatUserBySessionId($session_id);
  179. if (!$chatUser) {
  180. throw new ShoproException('删除失败');
  181. }
  182. $this->getter('db')->delCustomerByCustomerService($room_id, $chatUser['id'], $customerService, $is_del_record);
  183. $this->sender->successSocket($callback, '删除成功');
  184. });
  185. $this->register('del_customer_all', function ($data, $callback) {
  186. // 当前客服信息
  187. $room_id = $this->session('room_id');
  188. $customerService = $this->session('customer_service');
  189. $is_del_record = $data['is_del_record'] ?? false; // 是否删除聊天记录
  190. $this->getter('db')->delCustomerAllByCustomerService($room_id, $customerService, $is_del_record);
  191. $this->sender->successSocket($callback, '删除成功');
  192. });
  193. // 客服上线
  194. $this->register('customer_service_online', function ($data, $callback) {
  195. // 客服操作自己在线状态触发
  196. // 客服上线
  197. $this->customerServiceOnline();
  198. // 通知自己上线成功
  199. $this->sender->successSocket($callback, '当前状态已切换为在线', [
  200. 'customer_service' => $this->session('customer_service')
  201. ]);
  202. });
  203. // 客服离线
  204. $this->register('customer_service_offline', function ($data, $callback) {
  205. // 客服操作自己为离线状态触发
  206. // 客服离线
  207. $this->customerServiceOffline();
  208. // 通知自己离线成功
  209. $this->sender->successSocket($callback, '当前状态已切换为离线', [
  210. 'customer_service' => $this->session('customer_service')
  211. ]);
  212. });
  213. // 客服忙碌
  214. $this->register('customer_service_busy', function ($data, $callback) {
  215. // 客服操作自己在线状态触发
  216. // 客服忙碌
  217. $this->customerServiceBusy();
  218. // 通知自己离线成功
  219. $this->sender->successSocket($callback, '当前状态已切换为忙碌', [
  220. 'customer_service' => $this->session('customer_service')
  221. ]);
  222. });
  223. // 客服登录
  224. $this->register('customer_service_logout', function ($data, $callback) {
  225. $this->customerServiceLogout();
  226. // 解绑客服相关的事件,等下次客服再登录时再重新绑定
  227. $this->socket->removeAllListeners('customer_service_init');
  228. $this->socket->removeAllListeners('message');
  229. $this->socket->removeAllListeners('messages');
  230. $this->socket->removeAllListeners('access');
  231. $this->socket->removeAllListeners('transfer');
  232. $this->socket->removeAllListeners('break_customer');
  233. $this->socket->removeAllListeners('del_customer');
  234. $this->socket->removeAllListeners('del_customer_all');
  235. $this->socket->removeAllListeners('customer_service_online');
  236. $this->socket->removeAllListeners('customer_service_offline');
  237. $this->socket->removeAllListeners('customer_service_busy');
  238. $this->socket->removeAllListeners('customer_service_logout');
  239. $this->sender->successSocket($callback, '退出成功');
  240. });
  241. }
  242. /**
  243. * 客服上线,并通知连接的用户,和房间中的其他客服
  244. *
  245. * @return void
  246. */
  247. private function customerServiceOnline()
  248. {
  249. // 房间号
  250. $room_id = $this->session('room_id');
  251. // 客服信息
  252. $customerService = $this->session('customer_service');
  253. // 记录客服切换之前的在线状态
  254. $isOnLineCustomerService = $this->getter('socket')->isOnLineCustomerServiceById($customerService['id']);
  255. // 客服上线,更新客服状态,加入客服组
  256. $this->chatService->customerServiceOnline($room_id, $customerService['id']);
  257. // if (!$isOnLineCustomerService) {
  258. // (这里不判断,都通知,重复通知不影响)如果之前是离线状态
  259. // 通知连接的用户(在当前客服服务的房间里面的用户),客服上线了
  260. $this->sender->customerServiceOnline();
  261. // 通知当前房间的在线客服,更新当前在线客服列表
  262. $this->sender->customerServiceUpdate();
  263. // }
  264. }
  265. /**
  266. * 客服离线,并通知连接的用户,和房间中的其他客服
  267. *
  268. * @return void
  269. */
  270. private function customerServiceOffline()
  271. {
  272. // 房间号
  273. $room_id = $this->session('room_id');
  274. // 客服信息
  275. $customerService = $this->session('customer_service');
  276. // 客服下线,更新客服状态,解绑 client_id,退出客服组
  277. $this->chatService->customerServiceOffline($room_id, $customerService['id']);
  278. if (!$this->getter('socket')->isOnLineCustomerServiceById($customerService['id'])) {
  279. // 当前客服的所有客户端都下线了
  280. // 通知连接的用户(在当前客服服务的房间里面的用户),客服下线了
  281. $this->sender->customerServiceOffline();
  282. // 通知当前房间的在线客服,更新当前在线客服列表
  283. $this->sender->customerServiceUpdate();
  284. }
  285. }
  286. /**
  287. * 客服忙碌,如果之前客服是离线 通知连接的用户,和房间中的其他客服,上线了
  288. *
  289. * @return void
  290. */
  291. private function customerServiceBusy()
  292. {
  293. // 房间号
  294. $room_id = $this->session('room_id');
  295. // 客服信息
  296. $customerService = $this->session('customer_service');
  297. // 记录客服切换之前的在线状态
  298. $isOnLineCustomerService = $this->getter('socket')->isOnLineCustomerServiceById($customerService['id']);
  299. // 客服忙碌,更新客服状态,判断并加入客服组
  300. $this->chatService->customerServiceBusy($room_id, $customerService['id']);
  301. // if (!$isOnLineCustomerService) {
  302. // (这里不判断,都通知,重复通知不影响)如果之前是离线状态
  303. // 通知连接的用户(在当前客服服务的房间里面的用户),客服上线了
  304. $this->sender->customerServiceBusy();
  305. // 通知当前房间的在线客服,更新当前在线客服列表
  306. $this->sender->customerServiceUpdate();
  307. // }
  308. }
  309. /**
  310. * 客服退出登录
  311. *
  312. * @return void
  313. */
  314. public function customerServiceLogout()
  315. {
  316. // 房间号
  317. $room_id = $this->session('room_id');
  318. // 客服信息
  319. $customerService = $this->session('customer_service');
  320. $customer_service_id = $this->session('customer_service_id');
  321. // 客服先离线
  322. $this->chatService->customerServiceOffline($room_id, $customer_service_id);
  323. if (!$this->getter('socket')->isOnLineCustomerServiceById($customerService['id'])) {
  324. // 当前客服的所有客户端都下线了
  325. // 通知连接的用户(在当前客服服务的房间里面的用户),客服下线了
  326. $this->sender->customerServiceOffline();
  327. // 通知当前房间的在线客服,更新当前在线客服列表
  328. $this->sender->customerServiceUpdate();
  329. }
  330. // 客服退出房间,清除客服 session 信息
  331. $this->chatService->customerServiceLogout($room_id, $customer_service_id);
  332. }
  333. }