BaseHandler.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace app\webscoket;
  12. use app\services\message\service\StoreServiceLogServices;
  13. use app\services\message\service\StoreServiceRecordServices;
  14. use app\services\message\service\StoreServiceServices;
  15. use app\services\order\StoreOrderRefundServices;
  16. use app\services\order\StoreOrderServices;
  17. use app\services\product\product\StoreProductServices;
  18. use app\services\user\UserServices;
  19. use app\services\wechat\WechatUserServices;
  20. use app\jobs\notice\template\WechatTemplateJob;
  21. use crmeb\services\wechat\Messages;
  22. use crmeb\services\wechat\OfficialAccount;
  23. use crmeb\utils\Arr;
  24. use think\facade\Log;
  25. /**
  26. * socket 事件基础类
  27. * Class BaseHandler
  28. * @package app\webscoket
  29. */
  30. abstract class BaseHandler
  31. {
  32. /**
  33. * @var Manager
  34. */
  35. protected $manager;
  36. /**
  37. * @var Room
  38. */
  39. protected $room;
  40. /**
  41. * @var int
  42. */
  43. protected $fd;
  44. /**
  45. * 用户聊天端
  46. * @var int|null
  47. */
  48. protected $formType;
  49. /**
  50. * 登陆
  51. * @param array $data
  52. * @param Response $response
  53. * @return mixed
  54. */
  55. abstract public function login(array $data, Response $response);
  56. /**
  57. * 事件入口
  58. * @param $event
  59. * @return |null
  60. */
  61. public function handle($event)
  62. {
  63. [$method, $result, $manager, $room] = $event;
  64. $this->manager = $manager;
  65. $this->room = $room;
  66. $this->fd = array_shift($result);
  67. $this->formType = array_shift($result);
  68. if (method_exists($this, $method)) {
  69. if (($method == 'login' || $method == 'kefu_login') && is_string($result[0])) {
  70. $result[0] = ['token' => $result[0]];
  71. }
  72. return $this->{$method}(...$result);
  73. } else {
  74. Log::error('socket 回调事件' . $method . '不存在,消息内容为:' . json_encode($result));
  75. return null;
  76. }
  77. }
  78. /**
  79. * 聊天事件
  80. * @param array $data
  81. * @param Response $response
  82. * @return bool|\think\response\Json|null
  83. * @throws \think\db\exception\DataNotFoundException
  84. * @throws \think\db\exception\DbException
  85. * @throws \think\db\exception\ModelNotFoundException
  86. */
  87. public function chat(array $data = [], Response $response)
  88. {
  89. $user = $this->room->get($this->fd);
  90. if (!$user) {
  91. return $response->fail('聊天用户不存在');
  92. }
  93. $to_uid = $data['to_uid'] ?? 0;
  94. $msn_type = $data['type'] ?? 0;
  95. $msn = $data['msn'] ?? '';
  96. $formType = $this->formType ?? null;
  97. //是否为游客
  98. $isTourist = $data['is_tourist'] ?? 0;
  99. $tourist_uid = $data['tourist_uid'] ?? 0;
  100. $isTourist = $isTourist && $tourist_uid;
  101. $tourist_avatar = $data['tourist_avatar'] ?? '';
  102. $uid = $isTourist ? $tourist_uid : $user['uid'];
  103. if (!$to_uid) {
  104. return $response->message('err_tip', ['msg' => '用户不存在']);
  105. }
  106. if ($to_uid == $uid) {
  107. return $response->message('err_tip', ['msg' => '不能和自己聊天']);
  108. }
  109. /** @var StoreServiceLogServices $logServices */
  110. $logServices = app()->make(StoreServiceLogServices::class);
  111. if (!in_array($msn_type, StoreServiceLogServices::MSN_TYPE)) {
  112. return $response->message('err_tip', ['msg' => '格式错误']);
  113. }
  114. $msn = trim(strip_tags(str_replace(["\n", "\t", "\r", "&nbsp;"], '', htmlspecialchars_decode($msn))));
  115. $data = compact('to_uid', 'msn_type', 'msn', 'uid');
  116. $data['add_time'] = time();
  117. $data['is_tourist'] = $data['is_tourist'] ?? 0;
  118. //获取收消息人的所有fd信息
  119. $toUserFd = $this->manager->userFd($user['type'] == 2 ? 1 : 2, $to_uid);
  120. $toUser = ['to_uid' => -1];
  121. $fremaData = [];
  122. foreach ($toUserFd as $value) {
  123. if ($frem = $this->room->get($value)) {
  124. $fremaData[] = $frem;
  125. //如果当收消息人在和当前发消息人对话中
  126. if ($frem['to_uid'] == $user['uid']) {
  127. $toUser = $frem;
  128. }
  129. }
  130. }
  131. //是否在线
  132. //是否和当前用户对话
  133. $online = $toUserFd && $toUser && $toUser['to_uid'] !== -1;
  134. $data['type'] = $online ? 1 : 0;
  135. $data = $logServices->save($data);
  136. $data = $data->toArray();
  137. $data['_add_time'] = $data['add_time'];
  138. $data['add_time'] = strtotime($data['add_time']);
  139. $_userInfo = [];
  140. if (!$isTourist) {
  141. /** @var StoreServiceServices $userService */
  142. $userService = app()->make(StoreServiceServices::class);
  143. $_userInfo = $userService->get(['uid' => $data['uid']], ['nickname', 'avatar']);
  144. if (!$_userInfo) {
  145. /** @var UserServices $userService */
  146. $userService = app()->make(UserServices::class);
  147. $_userInfo = $userService->getUserInfo($data['uid'], 'nickname,avatar');
  148. }
  149. }
  150. if ($isTourist || !$_userInfo) {
  151. $avatar = sys_config('tourist_avatar');
  152. $_userInfo['avatar'] = $tourist_avatar ? $tourist_avatar : Arr::getArrayRandKey(is_array($avatar) ? $avatar : []);
  153. $_userInfo['nickname'] = '游客' . $uid;
  154. }
  155. $data['nickname'] = $_userInfo['nickname'];
  156. $data['avatar'] = $_userInfo['avatar'];
  157. //商品消息类型
  158. $data['productInfo'] = [];
  159. if ($msn_type == StoreServiceLogServices::MSN_TYPE_GOODS && $msn) {
  160. /** @var StoreProductServices $productServices */
  161. $productServices = app()->make(StoreProductServices::class);
  162. $productInfo = $productServices->getProductInfo((int)$msn, 'store_name,IFNULL(sales,0) + IFNULL(ficti,0) as sales,image,slider_image,price,vip_price,ot_price,stock,id');
  163. $data['productInfo'] = $productInfo ? $productInfo->toArray() : [];
  164. }
  165. //订单消息类型
  166. $data['orderInfo'] = [];
  167. if ($msn_type == StoreServiceLogServices::MSN_TYPE_ORDER && $msn) {
  168. /** @var StoreOrderServices $orderServices */
  169. $orderServices = app()->make(StoreOrderServices::class);
  170. $order = $orderServices->getUserOrderDetail($msn, $uid);
  171. if ($order) {
  172. $order = $orderServices->tidyOrder($order->toArray(), true, true);
  173. $order['add_time_y'] = date('Y-m-d', $order['add_time']);
  174. $order['add_time_h'] = date('H:i:s', $order['add_time']);
  175. $data['orderInfo'] = $order;
  176. }
  177. }
  178. if ($msn_type == StoreServiceLogServices::MSN_TYPE_REFUND_ORDER && $msn) {
  179. /** @var StoreOrderRefundServices $orderRefundServices */
  180. $orderRefundServices = app()->make(StoreOrderRefundServices::class);
  181. $order = $orderRefundServices->refundDetail($msn);
  182. if ($order) {
  183. $order['add_time_y'] = date('Y-m-d', $order['add_time']);
  184. $order['add_time_h'] = date('H:i:s', $order['add_time']);
  185. $data['orderInfo'] = $order;
  186. }
  187. }
  188. //用户向客服发送消息,判断当前客服是否在登录中
  189. /** @var StoreServiceRecordServices $serviceRecored */
  190. $serviceRecored = app()->make(StoreServiceRecordServices::class);
  191. $unMessagesCount = $logServices->getMessageNum(['uid' => $uid, 'to_uid' => $to_uid, 'type' => 0, 'is_tourist' => $isTourist ? 1 : 0]);
  192. //记录当前用户和他人聊天记录
  193. $data['recored'] = $serviceRecored->saveRecord($uid, $to_uid, $msn, $formType ?? 0, $msn_type, $unMessagesCount, $isTourist, $data['nickname'], $data['avatar']);
  194. //是否在线
  195. if ($online) {
  196. $this->manager->pushing($toUserFd, $response->message('reply', $data)->getData());
  197. } else {
  198. //用户在线,可是没有和当前用户进行聊天,给当前用户发送未读条数
  199. if ($toUserFd && $toUser && isset($toUser['to_uid']) && $toUser['to_uid'] != $uid) {
  200. $data['recored']['nickname'] = $_userInfo['nickname'];
  201. $data['recored']['avatar'] = $_userInfo['avatar'];
  202. $this->manager->pushing($toUserFd, $response->message('mssage_num', [
  203. 'uid' => $uid,
  204. 'num' => $unMessagesCount,
  205. 'recored' => $data['recored']
  206. ])->getData());
  207. }
  208. if (!$isTourist) {
  209. //用户不在线
  210. /** @var WechatUserServices $wechatUserServices */
  211. $wechatUserServices = app()->make(WechatUserServices::class);
  212. $userInfo = $wechatUserServices->getOne(['uid' => $to_uid, 'user_type' => 'wechat'], 'nickname,subscribe,openid,headimgurl');
  213. if ($userInfo && $userInfo['subscribe'] && $userInfo['openid']) {
  214. $description = '您有新的消息,请注意查收!';
  215. if ($formType !== null) {
  216. $head = '客服接待消息提醒';
  217. $url = sys_config('site_url') . '/kefu/mobile_chat?toUid=' . $uid . '&nickname=' . $_userInfo['nickname'];
  218. } else {
  219. $head = '客服回复消息提醒';
  220. $url = sys_config('site_url') . '/pages/extension/customer_list/chat?uid=' . $uid;
  221. }
  222. $message = Messages::newsMessage($head, $description, $url, $_userInfo['avatar']);
  223. $userInfo = $userInfo->toArray();
  224. try {
  225. OfficialAccount::staffService()->message($message)->to($userInfo['openid'])->send();
  226. } catch (\Exception $e) {
  227. if ($msn_type == 3) {
  228. $msn = '[图片]';
  229. } elseif ($msn_type == 4) {
  230. $msn = '[语音]';
  231. } elseif ($msn_type == 5) {
  232. $msn = '[商品]';
  233. } elseif ($msn_type == 6) {
  234. $msn = '[订单]';
  235. } elseif ($msn_type == 7) {
  236. $msn = '[退款订单]';
  237. }
  238. // $res = WechatTemplateJob::dispatchDo('sendServiceNoticeNew', [$userInfo['openid'], [
  239. // 'first' => $head,
  240. // 'keyword1' => $formType !== null ? '客服接待' : '客服回复',
  241. // 'keyword2' => '回复中',
  242. // 'keyword3' => date('Y-m-d H:i:s', time()),
  243. // 'remark' => '消息内容:' . $msn . ';点击查看更多消息!'
  244. // ], $url]);
  245. // if (!$res) Log::error($userInfo['nickname'] . '发送失败' . $e->getMessage());
  246. }
  247. }
  248. }
  249. }
  250. return $response->message('chat', $data);
  251. }
  252. /**
  253. * 切换用户聊天
  254. * @param array $data
  255. * @param Response $response
  256. * @return \think\response\Json
  257. */
  258. public function to_chat(array $data = [], Response $response)
  259. {
  260. $toUid = $data['id'] ?? 0;
  261. $res = $this->room->get($this->fd);
  262. if ($res && $toUid) {
  263. $uid = $res['uid'];
  264. $this->manager->updateTabelField((int)$res['type'], (int)$uid, (int)$toUid);
  265. //不是游客进入记录
  266. if (!$res['tourist']) {
  267. /** @var StoreServiceRecordServices $service */
  268. $service = app()->make(StoreServiceRecordServices::class);
  269. $service->update(['user_id' => $uid, 'to_uid' => $toUid], ['mssage_num' => 0]);
  270. /** @var StoreServiceLogServices $logServices */
  271. $logServices = app()->make(StoreServiceLogServices::class);
  272. $logServices->update(['uid' => $toUid, 'to_uid' => $uid], ['type' => 1]);
  273. }
  274. return $response->message('mssage_num', ['uid' => $toUid, 'num' => 0, 'recored' => (object)[]]);
  275. }
  276. }
  277. /**
  278. * 测试原样返回
  279. * @param array $data
  280. * @param Response $response
  281. * @return bool|\think\response\Json|null
  282. */
  283. public function test(array $data = [], Response $response)
  284. {
  285. return $response->success($data);
  286. }
  287. /**
  288. * 关闭连接触发
  289. * @param array $data
  290. * @param Response $response
  291. */
  292. public function close(array $data = [], Response $response)
  293. {
  294. $uid = $data['uid'] ?? 0;
  295. if ($uid) {
  296. /** @var StoreServiceRecordServices $service */
  297. $service = app()->make(StoreServiceRecordServices::class);
  298. $service->updateRecord(['to_uid' => $uid], ['online' => 0]);
  299. $this->manager->pushing($this->manager->userFd(Manager::KEFU_TYPE_NUM), $response->message('online', [
  300. 'online' => 0,
  301. 'uid' => $uid
  302. ]), $this->fd);
  303. }
  304. }
  305. }