Db.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. <?php
  2. namespace addons\shopro\library\chat\provider\getter;
  3. use think\helper\Str;
  4. use addons\shopro\exception\ShoproException;
  5. use addons\shopro\library\chat\traits\DebugEvent;
  6. use addons\shopro\library\chat\Getter;
  7. use addons\shopro\controller\traits\UnifiedToken;
  8. use app\admin\model\shopro\chat\User as ChatUser;
  9. use app\admin\model\shopro\chat\ServiceLog as ChatServiceLog;
  10. use app\admin\model\shopro\chat\Record as ChatRecord;
  11. use app\admin\model\shopro\chat\CustomerService as ChatCustomerService;
  12. use app\admin\model\shopro\chat\CustomerServiceUser as ChatCustomerServiceUser;
  13. use app\admin\model\shopro\chat\Question as ChatQuestion;
  14. use app\admin\model\shopro\chat\CommonWord as ChatCommonWord;
  15. /**
  16. * 从数据库获取
  17. */
  18. class Db
  19. {
  20. /**
  21. * token 验证助手
  22. */
  23. use UnifiedToken;
  24. /**
  25. * getter 实例
  26. *
  27. * @var Getter
  28. */
  29. protected $getter;
  30. /**
  31. * 实例化的模型数组
  32. *
  33. * @var array
  34. */
  35. protected $models = [];
  36. public function __construct(Getter $getter)
  37. {
  38. $this->getter = $getter;
  39. }
  40. /**
  41. * 获取动态模型实例
  42. *
  43. * @param string $type 动态类型
  44. * @param string $model 模型名称
  45. * @param boolean $is_instance 是否实例化
  46. * @return string|object 返回模型
  47. */
  48. public function getModel($type, $model, $is_instance = true)
  49. {
  50. $key = $type . '_' . $model . '_' . strval($is_instance);
  51. if (isset($this->models[$key])) {
  52. return $this->models[$key];
  53. }
  54. switch($type) {
  55. case 'auth' :
  56. if ($model == 'user') {
  57. $class = '\\app\\admin\\model\\shopro\\user\\' . ucfirst($model);
  58. } else {
  59. $class = '\\app\\admin\\model\\shopro\\' . ucfirst($model);
  60. }
  61. break;
  62. }
  63. if ($is_instance) {
  64. $class = new $class;
  65. }
  66. $this->models[$key] = $class;
  67. return $class;
  68. }
  69. /**
  70. * 通过token 获取用户 id
  71. *
  72. * @param string $token
  73. * @param string $auth 认证类型
  74. * @return mixed
  75. */
  76. public function getAuthIdFromToken($token, $auth)
  77. {
  78. // 获取加密数据
  79. $content = $this->getUnifiedContent($token);
  80. // 判断并返回用户 id
  81. if ($content && strpos($content, $auth) !== false) {
  82. return str_replace($auth . ':', '', $content);
  83. }
  84. return false;
  85. }
  86. /**
  87. * 通过 token 获取 用户信息
  88. *
  89. * @param string $token
  90. * @param string $auth 认证类型
  91. * @return null|object
  92. */
  93. public function getAuthByToken($token, $auth)
  94. {
  95. // 获取用户 id
  96. $user_id = $this->getAuthIdFromToken($token, $auth);
  97. // 获取用户
  98. return $this->getAuthById($user_id, $auth);
  99. }
  100. /**
  101. * 通过 id 获取用户
  102. *
  103. * @param int $id
  104. * @param string $auth
  105. * @return null|object
  106. */
  107. public function getAuthById($id, $auth)
  108. {
  109. return $this->getModel('auth', $auth)->where('id', $id)->find();
  110. }
  111. /**
  112. * 通过 session_id 获取chat 用户
  113. *
  114. * @param string $session_id
  115. * @param string $auth 认证类型
  116. * @return null|object
  117. */
  118. public function getChatUserBySessionId($session_id)
  119. {
  120. $chatUser = ChatUser::where('session_id', $session_id)->order('id asc')->find();
  121. return $chatUser;
  122. }
  123. /**
  124. * 通过 auth 获取 library 用户信息
  125. *
  126. * @param integer $auth_id
  127. * @param string $auth
  128. * @return void
  129. */
  130. public function getChatUserByAuth($auth_id, $auth)
  131. {
  132. $chatUser = ChatUser::where('auth', $auth)->where('auth_id', $auth_id)->order('id asc')->find();
  133. return $chatUser;
  134. }
  135. /**
  136. * 获取最后一次被服务记录
  137. *
  138. * @param string $room_id
  139. * @param integer $chat_user_id
  140. * @return object|null
  141. */
  142. public function getLastServiceLogByChatUser($room_id, $chat_user_id)
  143. {
  144. return ChatServiceLog::where('room_id', $room_id)
  145. ->where('chat_user_id', $chat_user_id)
  146. ->where('customer_service_id', '<>', 0)
  147. ->order('id', 'desc')->find();
  148. }
  149. /**
  150. * 获取郑子昂服务中的记录
  151. *
  152. * @param string $room_id
  153. * @param integer $chat_user_id
  154. * @return object
  155. */
  156. public function getServiceLogIngByChatUser($room_id, $chat_user_id, $customer_service_id = 0)
  157. {
  158. return ChatServiceLog::where('room_id', $room_id)
  159. ->where('chat_user_id', $chat_user_id)
  160. ->where(function ($query) use ($customer_service_id) {
  161. $query->where(function($query) use ($customer_service_id) {
  162. $query->where('status', 'waiting')->where('customer_service_id', 0);
  163. })->whereOr(function ($query) use ($customer_service_id) {
  164. $query->where('status', 'ing')->where('customer_service_id', $customer_service_id);
  165. });
  166. })
  167. ->order('id', 'desc')->find();
  168. }
  169. /**
  170. * 添加服务记录
  171. *
  172. * @param string $room_id
  173. * @param array $chatUser
  174. * @param array $customerService
  175. * @param string $status
  176. * @return void
  177. */
  178. public function addServiceLog($room_id, $chatUser, $customerService, $status)
  179. {
  180. $chatServiceLog = new ChatServiceLog();
  181. $chatServiceLog->chat_user_id = $chatUser ? $chatUser['id'] : 0;
  182. $chatServiceLog->customer_service_id = $customerService ? $customerService['id'] : 0;
  183. $chatServiceLog->room_id = $room_id;
  184. $chatServiceLog->starttime = time();
  185. $chatServiceLog->endtime = $status == 'end' ? time() : null;
  186. $chatServiceLog->status = $status;
  187. $chatServiceLog->save();
  188. return $chatServiceLog;
  189. }
  190. /**
  191. * 创建正在进行中的 服务
  192. *
  193. * @param [type] $room_id
  194. * @param [type] $chatUser
  195. * @param [type] $customerService
  196. * @return void
  197. */
  198. public function createServiceLog($room_id, $chatUser, $customerService)
  199. {
  200. // 正在进行中的连接
  201. $serviceLog = $this->getServiceLogIngByChatUser($room_id, $chatUser['id'], $customerService['id']);
  202. if (!$serviceLog) {
  203. // 不存在,创建新的
  204. $serviceLog = $this->addServiceLog($room_id, $chatUser, $customerService, 'ing');
  205. }
  206. return $serviceLog;
  207. }
  208. /**
  209. * 结束 服务
  210. *
  211. * @param string $room_id 房间号
  212. * @param array $chatUser 顾客
  213. * @param array $customerService 客服
  214. * @return object
  215. */
  216. public function endServiceLog($room_id, $chatUser, $customerService)
  217. {
  218. // 正在进行中的连接
  219. $serviceLog = $this->getServiceLogIngByChatUser($room_id, $chatUser['id'], $customerService['id']);
  220. if (!$serviceLog) {
  221. // 不存在,创建新的
  222. $serviceLog = $this->addServiceLog($room_id, $chatUser, $customerService, 'end');
  223. } else {
  224. $serviceLog->customer_service_id = $customerService ? $customerService['id'] : 0;
  225. $serviceLog->endtime = time();
  226. $serviceLog->status = 'end';
  227. $serviceLog->save();
  228. }
  229. return $serviceLog;
  230. }
  231. /**
  232. * 通过用户获取到用户绑定的在指定房间的客服信息(判断 auth 在指定房间是否是客服,是了才能登录客服)
  233. *
  234. * @param string $room_id 房间号
  235. * @param string $auth 用户类型
  236. * @param integer $auth_id 用户 id
  237. * @return array|null
  238. */
  239. public function getCustomerServiceByAuthAndRoom($room_id, $auth_id, $auth)
  240. {
  241. // 获取当前 auth 绑定的所有客服的 id
  242. $customerServiceIds = ChatCustomerServiceUser::where('auth', $auth)->where('auth_id', $auth_id)->column('customer_service_id');
  243. // 通过上一步的 客服id 配合 房间获取第一条客服(只能有一条)
  244. $customerService = ChatCustomerService::where('room_id', $room_id)->where('id', 'in', $customerServiceIds)->find();
  245. return $customerService;
  246. }
  247. /**
  248. * 通过用户获取到用户绑定的所有客服信息(暂不使用,当独立登录是,让用户选择客服身份)
  249. *
  250. * @param string $auth 用户类型
  251. * @param integer $auth_id 用户 id
  252. * @return array
  253. */
  254. public function getCustomerServicesByAuth($auth_id, $auth, $first = false)
  255. {
  256. // 获取当前 auth 绑定的所有客服的 id
  257. $customerServiceIds = ChatCustomerServiceUser::where('auth', $auth)->where('auth_id', $auth_id)->column('customer_service_id');
  258. // 通过上一步的 客服id 配合 房间获取第一条客服(只能有一条)
  259. $customerServices = ChatCustomerService::where('id', 'in', $customerServiceIds)->order('id', 'asc')->select();
  260. return $first ? ($customerServices[0] ?? null) : $customerServices;
  261. }
  262. /**
  263. * 获取客服服务的历史用户
  264. *
  265. * @param string $room_id 房间号
  266. * @param integer $customer_service_id 客服 id
  267. * @param array $exceptIds 要排除的ids (正在服务的用户)
  268. * @return array
  269. */
  270. public function getCustomersHistoryByCustomerService($room_id, $customer_service_id, $exceptIds = [])
  271. {
  272. // $logs = ChatServiceLog::with('chat_user')
  273. // ->where('room_id', $room_id)
  274. // ->field('chat_user_id')
  275. // ->whereNotIn('chat_user_id', $exceptIds)
  276. // ->where('customer_service_id', $customer_service_id)
  277. // ->group('chat_user_id')
  278. // ->order('id', 'desc')
  279. // ->select();
  280. // $logs = collection($logs)->toArray();
  281. // $chatUsers = array_column($logs, 'chat_user');
  282. // 替代上面的方法,上面方法 group by 在 mysql 严格模式必须要关闭 ONLY_FULL_GROUP_BY
  283. $chatUsers = [];
  284. $logs = ChatServiceLog::with('chat_user')
  285. ->field('id,chat_user_id')
  286. ->where('room_id', $room_id)
  287. ->whereNotIn('chat_user_id', $exceptIds)
  288. ->where('customer_service_id', $customer_service_id)
  289. ->chunk(100, function ($currentLogs) use (&$chatUsers) {
  290. foreach ($currentLogs as $log) {
  291. $chatUser = $log->chat_user;
  292. $currentIds = array_column($chatUsers, 'id');
  293. if ($chatUser && !in_array($chatUser->id, $currentIds)) {
  294. $chatUsers[] = $chatUser;
  295. }
  296. if (count($chatUsers) >= 20) {
  297. break;
  298. }
  299. }
  300. if (count($chatUsers) >= 20) {
  301. return false;
  302. }
  303. }, 'id', 'desc'); // 如果 id 重复,有坑 (date < 2020-03-28)
  304. return $chatUsers;
  305. }
  306. /**
  307. * 通过 session_id 获取 顾客
  308. *
  309. * @param string $session_id
  310. * @return object
  311. */
  312. public function getCustomerBySessionId($session_id)
  313. {
  314. return ChatUser::where('session_id', $session_id)->find();
  315. }
  316. /**
  317. * 删除客服的一个顾客的所有服务记录
  318. *
  319. * @param string $room_id
  320. * @param integer $chat_user_id
  321. * @param array $customerService
  322. * @param boolean $is_del_record
  323. * @return void
  324. */
  325. public static function delCustomerByCustomerService($room_id, $chat_user_id, $customerService, $is_del_record = false)
  326. {
  327. ChatServiceLog::where('room_id', $room_id)
  328. ->where('customer_service_id', $customerService['id'])
  329. ->where('chat_user_id', $chat_user_id)->delete();
  330. if ($is_del_record) {
  331. self::delCustomerRecordById($room_id, $chat_user_id);
  332. }
  333. }
  334. /**
  335. * 删除客服的一个顾客的所有服务记录
  336. *
  337. * @param string $room_id
  338. * @param array $customerService
  339. * @param boolean $is_del_record
  340. * @return void
  341. */
  342. public static function delCustomerAllByCustomerService($room_id, $customerService, $is_del_record = false)
  343. {
  344. if ($is_del_record) {
  345. $chatUserIds = ChatServiceLog::where('room_id', $room_id)
  346. ->where('customer_service_id', $customerService['id'])->column('chat_user_id');
  347. $chatUserIds = array_values(array_unique($chatUserIds));
  348. foreach ($chatUserIds as $chat_user_id) {
  349. self::delCustomerRecordById($room_id, $chat_user_id);
  350. }
  351. }
  352. ChatServiceLog::where('room_id', $room_id)
  353. ->where('customer_service_id', $customerService['id'])->delete();
  354. }
  355. /**
  356. * 删除客户聊天记录
  357. *
  358. * @param string $room_id
  359. * @param int $chat_user_id
  360. * @return void
  361. */
  362. public static function delCustomerRecordById($room_id, $chat_user_id)
  363. {
  364. ChatRecord::where('room_id', $room_id)->where('chat_user_id', $chat_user_id)->delete();
  365. }
  366. /**
  367. * 获取顾客的聊天记录
  368. *
  369. * @param string $room_id
  370. * @param string $session_id
  371. * @param $select_identify
  372. * @return array
  373. */
  374. public function getCustomerMessagesBySessionId($room_id, $session_id, $select_identify, $data)
  375. {
  376. $selectIdentify = Str::camel($select_identify);
  377. $customer = $this->getCustomerBySessionId($session_id);
  378. $chat_user_id = $customer ? $customer['id'] : 0;
  379. // 将消息标记为已读
  380. ChatRecord::where('room_id', $room_id)->where('chat_user_id', $chat_user_id)->{$selectIdentify}()->update([
  381. 'read_time' => time()
  382. ]);
  383. $page = $data['page'] ?? 1;
  384. $list_rows = $data['list_rows'] ?? 20;
  385. $last_id = $data['last_id'] ?? 0;
  386. $messageList = ChatRecord::where('room_id', $room_id)->where('chat_user_id', $chat_user_id);
  387. // 避免消息重复
  388. if ($last_id) {
  389. $messageList = $messageList->where('id', '<=', $last_id);
  390. }
  391. $messageList = $messageList->order('id', 'desc')->paginate([
  392. 'page' => $page,
  393. 'list_rows' => $list_rows
  394. ]);
  395. $messageList = $this->getMessageSender($messageList);
  396. return $messageList;
  397. }
  398. /**
  399. * 获取用户的最后一条消息(当前房间的)
  400. *
  401. * @param string $room_id
  402. * @param integer $chat_user_id
  403. * @return object
  404. */
  405. public function getMessageLastByChatUser($room_id, $chat_user_id)
  406. {
  407. return ChatRecord::where('room_id', $room_id)->where('chat_user_id', $chat_user_id)->order('id', 'desc')->find();
  408. }
  409. /**
  410. * 根据身份获取未读消息条数(当前房间的)
  411. *
  412. * @param string $room_id
  413. * @param integer $chat_user_id
  414. * @param string $select_identify
  415. * @return integer
  416. */
  417. public function getMessageUnReadNumByChatUserAndIndentify($room_id, $chat_user_id, $select_identify)
  418. {
  419. $selectIdentify = Str::camel($select_identify);
  420. return ChatRecord::where('room_id', $room_id)->where('chat_user_id', $chat_user_id)->where('read_time', 'null')->{$selectIdentify}()->count();
  421. }
  422. /**
  423. * 更新客服信息
  424. *
  425. * @param integer $id
  426. * @param array $data
  427. * @return void
  428. */
  429. public function updateCustomerService($id, $data)
  430. {
  431. $customerService = ChatCustomerService::where('id', $id)->update($data);
  432. }
  433. /**
  434. * 添加消息记录
  435. *
  436. * @param string $room_id
  437. * @param string $name
  438. * @param array $arguments
  439. * @return object
  440. */
  441. public function addMessage($room_id, $name, $arguments)
  442. {
  443. $content = $arguments[2] ?? []; // 额外参数
  444. $message = $content['message'];
  445. $sender = $content['sender'];
  446. $sender_identify = $sender['sender_identify'] ?? 'customer';
  447. $receive = $arguments[3] ?? [];
  448. if ($sender_identify == 'customer') {
  449. $session_id = $sender['session_id'];
  450. $chatUser = $this->getChatUserBySessionId($session_id);
  451. $sender_id = $chatUser['id'] ?? 0;
  452. $chat_user_id = $sender_id;
  453. } else {
  454. $customerService = $sender['customer_service'];
  455. $sender_id = $customerService['id'] ?? 0;
  456. $session_id = $receive['id'];
  457. $chatUser = $this->getChatUserBySessionId($session_id);
  458. $chat_user_id = $chatUser['id'] ?? 0;
  459. }
  460. $chatRecord = new ChatRecord();
  461. $chatRecord->chat_user_id = $chat_user_id;
  462. $chatRecord->room_id = $room_id;
  463. $chatRecord->sender_identify = $sender_identify;
  464. $chatRecord->sender_id = $sender_id;
  465. $chatRecord->message_type = $message['message_type'] ?? 'text';
  466. $chatRecord->message = $message['message'] ?? '';
  467. $chatRecord->createtime = time();
  468. $chatRecord->updatetime = time();
  469. $chatRecord->save();
  470. // 加载消息人
  471. $chatRecord = $this->getMessageSender([$chatRecord])[0];
  472. return $chatRecord;
  473. }
  474. /**
  475. * 获取猜你想问
  476. *
  477. * @param string $room_id 房间
  478. * @return object
  479. */
  480. public function getChatQuestions($room_id)
  481. {
  482. $chatQuestions = ChatQuestion::roomId($room_id)->order(['weigh' => 'desc', 'id' => 'desc'])->select();
  483. return $chatQuestions;
  484. }
  485. /**
  486. * 根据 id 获取猜你想问
  487. *
  488. * @param string $room_id 房间
  489. * @return object
  490. */
  491. public function getChatQuestion($room_id, $question_id)
  492. {
  493. if ($question_id) {
  494. $chatQuestion = ChatQuestion::roomId($room_id)->find($question_id);
  495. return $chatQuestion;
  496. }
  497. return null;
  498. }
  499. /**
  500. * 获取客服常用语
  501. *
  502. * @param string $room_id 房间
  503. * @return object
  504. */
  505. public function getChatCommonWords($room_id)
  506. {
  507. $chatCommonWords = ChatCommonWord::normal()->roomId($room_id)->order(['weigh' => 'desc', 'id' => 'desc'])->select();
  508. return $chatCommonWords;
  509. }
  510. /**
  511. * 获取消息的 发送人
  512. *
  513. * @param array|object $messageList
  514. * @return array|object
  515. */
  516. private function getMessageSender($messageList)
  517. {
  518. $morphs = [
  519. 'customer' => \app\admin\model\shopro\chat\User::class,
  520. 'customer_service' => \app\admin\model\shopro\chat\CustomerService::class
  521. ];
  522. $messageList = morph_to($messageList, $morphs, ['sender_identify', 'sender_id']);
  523. return $messageList;
  524. }
  525. }