Common.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. <?php
  2. namespace addons\fastchat\library;
  3. use GatewayWorker\Lib\Gateway;
  4. use think\Db;
  5. /**
  6. *
  7. */
  8. class Common
  9. {
  10. function __construct()
  11. {
  12. }
  13. /**
  14. * 直接添加会话
  15. * @param string $user_id 会话对象
  16. * @return array
  17. */
  18. public static function add_session($user_id)
  19. {
  20. $session_user = self::user_info($user_id);
  21. $session = [
  22. 'user_id' => $_SESSION['user_id'],
  23. 'session_type' => $session_user['session_type'],
  24. 'session_user_id' => $session_user['id'],
  25. 'createtime' => time(),
  26. 'deletetime' => NULL,
  27. 'deleteuser' => ''
  28. ];
  29. Db::name('fastchat_session')->insert($session);
  30. $session['id'] = Db::name('fastchat_session')->getLastInsID();
  31. return $session;
  32. }
  33. /**
  34. * 获取一个用户的基本信息
  35. * @param string user 待标识符的用户id
  36. * @return array
  37. */
  38. public static function user_info($user)
  39. {
  40. $user = explode('||', $user);
  41. if (isset($user[1])) {
  42. if ($user[1] == 'user' || $user[1] == 'admin') {
  43. $user_info = Db::name($user[1])->where('id', $user[0])->find();
  44. $user_info['source'] = $user[1];
  45. $user_info['session_type'] = ($user[1] == 'user') ? 0 : 1;
  46. } elseif ($user[1] == 'tourists') {
  47. $user_info = Db::name('fastchat_tourists')->where('id', $user[0])->find();
  48. $user_info['source'] = $user[1];
  49. $user_info['session_type'] = 2;
  50. } elseif ($user[1] == 'service_user') {
  51. $user_info = Db::name('fastchat_service_user')->where('id', $user[0])->find();
  52. $user_info['session_type'] = 5;
  53. } elseif ($user[1] == 'massager') {
  54. $user_info = Db::name("massager")->where("id", $user[0])->find();
  55. $avatar = '';
  56. if (isset($user_info["photo_images"]) && mb_strlen($user_info["photo_images"]) > 0) {
  57. $avatar = explode(",", $user_info["photo_images"])[0];
  58. }
  59. $user_info["avatar"] = $avatar;
  60. $user_info["nickname"] = isset($user_info['name']) ? $user_info['name'] : "未知";
  61. $user_info['session_type'] = 6;
  62. }
  63. if (!isset($user_info) || !isset($user_info['id'])) {
  64. $user_info['id'] = $user[0];
  65. $user_info['source'] = 'unknown';
  66. $user_info['nickname'] = '未知用户' . $user[0];
  67. $user_info['avatar'] = false;
  68. $user_info['session_type'] = 4;
  69. }
  70. $user_info['avatar'] = self::img_src_fill($user_info['avatar']);
  71. } else {
  72. $user_info['id'] = $user[0];
  73. $user_info['source'] = 'none';
  74. $user_info['nickname'] = '未知' . $user[0];
  75. $user_info['avatar'] = self::img_src_fill(false);
  76. $user_info['session_type'] = 3;
  77. }
  78. return $user_info;
  79. }
  80. /**
  81. * 获取图片的完整地址
  82. * @param string src 待处理的图片
  83. * @return string
  84. */
  85. public static function img_src_fill($src)
  86. {
  87. $upload = \app\common\model\Config::upload();
  88. \think\Hook::listen("upload_config_init", $upload);
  89. $view_cdn = config('view_replace_str.__CDN__');
  90. $view_cdn = $view_cdn ? $view_cdn : cdnurl('', true);
  91. $domain = $upload['cdnurl'] ?? $view_cdn;
  92. return $src ? cdnurl($src, $domain) : $domain . '/assets/img/avatar.png';
  93. }
  94. /**
  95. * 发送消息
  96. * @param string user_id 带标识符的发送人
  97. * @param string session_user_id 带标识符的接受人
  98. * @param string message 消息内容
  99. * @return array
  100. */
  101. public static function send_message($user_id, $session_user_id, $message)
  102. {
  103. // 检查黑名单
  104. $blacklist = Db::name('fastchat_blacklist')
  105. ->where('user_id', $session_user_id)
  106. ->where('session_user_id', $user_id)
  107. ->value('id');
  108. if ($blacklist) {
  109. return ['msgtype' => 'send_message', 'code' => 0, 'msg' => '发送失败,对方已屏蔽您的发言!'];
  110. }
  111. // 检查是否有会话,没有则添加,有则直接发送消息
  112. $user = self::user_info($user_id);
  113. $session_user = self::user_info($session_user_id);
  114. $session = Db::name('fastchat_session')->where(function ($query) use ($user_id, $session_user) {
  115. $query->where('user_id', $user_id)
  116. ->where('session_type', $session_user['session_type'])
  117. ->where('session_user_id', $session_user['id']);
  118. })->whereOr(function ($query) use ($session_user_id, $user) {
  119. $query->where('user_id', $session_user_id)
  120. ->where('session_type', $user['session_type'])
  121. ->where('session_user_id', $user['id']);
  122. })
  123. ->find();
  124. if (!$session) {
  125. $session = [
  126. 'user_id' => $user_id,
  127. 'session_type' => $session_user['session_type'],
  128. 'session_user_id' => $session_user['id'],
  129. 'createtime' => time(),
  130. 'deletetime' => NULL,
  131. 'deleteuser' => ''
  132. ];
  133. Db::name('fastchat_session')->insert($session);
  134. $session['id'] = Db::name('fastchat_session')->getLastInsID();
  135. }
  136. $message_text = strip_tags($message, '<br><div><img><a>');
  137. $message = [
  138. 'session_id' => $session['id'],
  139. 'sender' => $user_id,
  140. 'message' => $message_text,
  141. 'createtime' => time()
  142. ];
  143. if (Db::name('fastchat_record')->insert($message)) {
  144. $message['record_id'] = Db::name('fastchat_record')->getLastInsID();//消息记录ID
  145. // 确定会话状态
  146. Db::name('fastchat_session')->where('id', $session['id'])->update(['deleteuser' => '', 'deletetime' => NULL, 'createtime' => time()]);
  147. if (Gateway::isUidOnline($session_user_id)) {
  148. // 加上发信人的信息
  149. $message['avatar'] = $user['avatar'];
  150. $message['nickname'] = $user['nickname'];
  151. $message['session_user'] = $user_id;
  152. $message['online'] = 1;
  153. $message['id'] = $message['session_id'];// 兼容前台
  154. unset($message['session_id']);
  155. $message['last_message'] = self::session_message($message_text);
  156. $message['last_time'] = self::format_time(NULL);
  157. $message['message'] = htmlspecialchars_decode($message['message']);
  158. // 查询当前用户发送的未读消息条数
  159. $message['unread_msg_count'] = Db::name('fastchat_record')
  160. ->where('session_id', $message['id'])
  161. ->where('sender', $user_id)
  162. ->where('status', 0)
  163. ->count('id');
  164. $message['sender'] = 'you';
  165. Gateway::sendToUid($session_user_id, json_encode(['msgtype' => 'new_message', 'data' => $message]));
  166. } else {
  167. // 检查是否有绑定服务号
  168. if ($session_user['session_type'] == 1) {
  169. $service_user = Db::name('fastchat_service_user')
  170. ->where('admin_id', $session_user['id'])
  171. ->where('status', '1')
  172. ->where('deletetime', NULL)
  173. ->find();
  174. if ($service_user) {
  175. $msg = '对方正使用服务账号【' . $service_user['nickname'] . '】接收消息,您的消息可能无法被对方查看!';
  176. return ['msgtype' => 'send_message', 'code' => 0, 'msg' => $msg];
  177. }
  178. }
  179. }
  180. return ['msgtype' => 'send_message', 'code' => 1];
  181. } else {
  182. return ['msgtype' => 'send_message', 'code' => 0, 'msg' => '发送失败,请重试!'];
  183. }
  184. }
  185. /**
  186. * 格式化消息-将图片和连接用文字代替
  187. * @param string message 消息内容
  188. * @return string
  189. */
  190. public static function session_message($message)
  191. {
  192. $message = htmlspecialchars_decode($message);
  193. $message = preg_replace("/<img(.*)>/", '[图片]', $message);
  194. $message = preg_replace("/<a(.*)<\/a>/", '[链接]', $message);
  195. return $message;
  196. }
  197. /**
  198. * 格式化时间
  199. * @param int time 时间戳
  200. * @return string
  201. */
  202. public static function format_time($time = NULL)
  203. {
  204. $text = '';
  205. $now_time = time();
  206. $time = ($time === NULL || $time > $now_time || $time == $now_time) ? $now_time + 1 : intval($time);
  207. $t = (int)($now_time - $time); //时间差 (秒)
  208. $y = date('Y', $time) - date('Y', $now_time);//是否跨年
  209. switch ($t) {
  210. case $t <= 0:
  211. $text = '刚刚';
  212. break;
  213. case $t < 60:
  214. $text = $t . '秒前'; // 一分钟内
  215. break;
  216. case $t < 60 * 60:
  217. $text = floor($t / 60) . '分钟前'; //一小时内
  218. break;
  219. case $t < 60 * 60 * 24:
  220. $text = floor($t / (60 * 60)) . '小时前'; // 一天内
  221. break;
  222. case $t < 60 * 60 * 24 * 3:
  223. $text = floor($time / (60 * 60 * 24)) == 1 ? '昨天' . date('H:i', $time) : '前天' . date('H:i', $time); //昨天和前天
  224. break;
  225. case $t < 60 * 60 * 24 * 30:
  226. $text = date('m-d H:i', $time); //一个月内
  227. break;
  228. case $t < 60 * 60 * 24 * 365 && $y == 0:
  229. $text = date('m-d', $time); //一年内
  230. break;
  231. default:
  232. $text = date('Y-m-d', $time); //一年以前
  233. break;
  234. }
  235. return $text;
  236. }
  237. /**
  238. * 用户token加密
  239. * @param string $token 待加密的token
  240. */
  241. public static function getEncryptedToken($token)
  242. {
  243. $token_config = \think\Config::get('token');
  244. $config = array(
  245. // 缓存前缀
  246. 'key' => $token_config['key'],
  247. // 加密方式
  248. 'hashalgo' => $token_config['hashalgo']
  249. );
  250. return hash_hmac($config['hashalgo'], $token, $config['key']);
  251. }
  252. /**
  253. * 检查用户身份
  254. * @param string user_cookie 用户cookie
  255. * @param int user_id 用户ID直接登录
  256. */
  257. public static function check_fa_user($user_cookie = null, $user_id = 0)
  258. {
  259. if ($user_cookie && !$user_id) {
  260. $cookie_httponly = config('cookie.httponly');
  261. if (!$cookie_httponly) {
  262. $user_id = Db::name('user_token')->where('token', Common::getEncryptedToken($user_cookie))->value('user_id');
  263. } else {
  264. list($id, $key) = explode('|', $user_cookie);
  265. $user_token_list = Db::name('user_token')
  266. ->where('user_id', $id)
  267. ->where('expiretime', '>', time())
  268. ->select();
  269. foreach ($user_token_list as $user_token) {
  270. $sign = $user_token['token'] . 'fastchat_user_sign_additional';
  271. $user_key = md5(md5($id) . md5($sign));
  272. if ($user_key == $key) {
  273. $user_id = $id;
  274. break;
  275. } else {
  276. $user_id = false;
  277. }
  278. }
  279. }
  280. }
  281. if ($user_id) {
  282. $user_info = Db::name('user')
  283. ->field('password,salt,token', true)
  284. ->where('id', $user_id)
  285. ->find();
  286. if ($user_info) {
  287. $user_info['user_id'] = $user_info['id'] . '||user';
  288. return $user_info;
  289. } else {
  290. return false;
  291. }
  292. } else {
  293. return false;
  294. }
  295. }
  296. /**
  297. * 检查助教身份
  298. * @param string $token
  299. * @param int user_id 用户ID直接登录
  300. */
  301. public static function check_massager($token = null)
  302. {
  303. if ($token) {
  304. $massager_info = Db::name('massager')
  305. ->where('session_token', $token)
  306. ->find();
  307. if ($massager_info) {
  308. $massager_info['user_id'] = $massager_info['id'] . '||massager';
  309. return $massager_info;
  310. }
  311. }
  312. return false;
  313. }
  314. /**
  315. * 检查管理员身份
  316. * @param string keeplogin 管理员cookie信息
  317. * @param int admin_id 管理员ID直接登录
  318. * @return array
  319. */
  320. public static function check_admin($keeplogin, $admin_id = 0)
  321. {
  322. if ($admin_id) {
  323. $admin = Db::name('admin')->field(['password', 'salt'], true)->where('id', $admin_id)->find();
  324. if (!$admin) {
  325. return false;
  326. }
  327. } else {
  328. list($id, $keeptime, $expiretime, $key) = explode('|', $keeplogin);
  329. if ($id && $keeptime && $expiretime && $key && $expiretime > time()) {
  330. $admin = Db::name('admin')->where('id', $id)->find();
  331. if (!$admin || !$admin['token']) {
  332. return false;
  333. }
  334. //token有变更
  335. $sign = $id . 'fastchat_admin_sign_additional';
  336. if ($key != md5(md5($sign) . md5($keeptime) . md5($expiretime) . $admin['token'])) {
  337. return false;
  338. }
  339. } else {
  340. return false;
  341. }
  342. }
  343. // 检查是否与服务号绑定
  344. $service_user = Db::name('fastchat_service_user')
  345. ->where('admin_id', $admin['id'])
  346. ->where('status', '1')
  347. ->where('deletetime', NULL)
  348. ->find();
  349. if ($service_user) {
  350. $service_user['username'] = $service_user['nickname'];
  351. $service_user['user_id'] = $service_user['id'] . '||service_user';
  352. $service_user['token'] = $admin['token'];
  353. return $service_user;
  354. }
  355. $admin['user_id'] = $admin['id'] . '||admin';
  356. $admin['admin_id'] = $admin['id'];// 和服务号字段统一
  357. return $admin;
  358. }
  359. /**
  360. * 将游客的会话转到用户
  361. * @param string tourists 游客的cookie信息
  362. * @param string user_id 带标识符的用户id
  363. * @return bool
  364. */
  365. public static function tourists_to_login($tourists, $user_id)
  366. {
  367. $user = self::user_info($user_id);//登录人信息
  368. $tourists_info = self::check_tourists($tourists);//游客信息
  369. if (!$tourists_info) {
  370. return;
  371. }
  372. // 查出游客的所有会话-》把 user_id 没有的会话转给 user_id
  373. $session_list = Db::name('fastchat_session')
  374. ->where(function ($query) use ($tourists_info) {
  375. $query->where('user_id', $tourists_info['user_id']);
  376. })->whereOr(function ($query) use ($tourists_info) {
  377. $query->where('session_user_id', $tourists_info['id'])->where('session_type', 2);
  378. })
  379. ->order('createtime desc')
  380. ->select();
  381. foreach ($session_list as $key => $value) {
  382. $session_user = Common::session_user($value, $tourists_info['user_id']);//本会话的会话对象
  383. $session_user_info = Common::user_info($session_user);//会话对象信息
  384. $session = Db::name('fastchat_session')->where(function ($query) use ($user_id, $session_user_info) {
  385. $query->where('user_id', $user_id)
  386. ->where('session_type', $session_user_info['session_type'])
  387. ->where('session_user_id', $session_user_info['id']);
  388. })->whereOr(function ($query) use ($session_user, $user) {
  389. $query->where('user_id', $session_user)
  390. ->where('session_type', $user['session_type'])
  391. ->where('session_user_id', $user['id']);
  392. })
  393. ->find();
  394. if (!$session) {
  395. // 将此会话转给用户
  396. if ($value['user_id'] == $tourists_info['user_id']) {
  397. Db::name('fastchat_session')
  398. ->where('id', $value['id'])
  399. ->update(['user_id' => $user_id]);
  400. } else {
  401. Db::name('fastchat_session')
  402. ->where('id', $value['id'])
  403. ->update(['session_user_id' => $user['id'], 'session_type' => $user['session_type']]);
  404. }
  405. Db::name('fastchat_record')
  406. ->where('session_id', $value['id'])
  407. ->where('sender', $tourists_info['user_id'])
  408. ->update(['sender' => $user_id]);
  409. }
  410. }
  411. return true;
  412. }
  413. /**
  414. * 检查游客身份
  415. * @param string tourists 游客的cookie信息
  416. * @return array
  417. */
  418. public static function check_tourists($tourists)
  419. {
  420. list($id, $keeptime, $expiretime, $key) = explode('|', $tourists);
  421. if ($id && $keeptime && $expiretime && $key && $expiretime > time()) {
  422. $tourists = Db::name('fastchat_tourists')->where('id', $id)->find();
  423. if (!$tourists || !$tourists['token']) {
  424. return false;
  425. }
  426. //token有变更
  427. if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $tourists['token'])) {
  428. return false;
  429. }
  430. } else {
  431. return false;
  432. }
  433. $tourists['user_id'] = $tourists['id'] . '||tourists';
  434. $tourists['username'] = $tourists['nickname'];
  435. unset($tourists['token']);
  436. return $tourists;
  437. }
  438. /**
  439. * 获取用户的未读消息->获取他的会话->获取会话中的非他自己发送的未读消息
  440. * @param string user_id 带标识符的用户id
  441. * @param bool is_latest 是否只获取用户未链接websocket时的消息
  442. * @return string
  443. */
  444. public static function get_unread_messages($user_id, $is_latest = false)
  445. {
  446. $new_msg = '';
  447. $user = self::user_info($user_id);
  448. $session_list = Db::name('fastchat_session')->where(function ($query) use ($user_id) {
  449. $query->where('user_id', $user_id);
  450. })->whereOr(function ($query) use ($user) {
  451. $query->where('session_user_id', $user['id'])->where('session_type', $user['session_type']);
  452. })
  453. ->order('createtime desc')
  454. ->select();
  455. foreach ($session_list as $key => $value) {
  456. $session_user = self::session_user($value, $user_id);
  457. $where['session_id'] = $value['id'];
  458. $where['sender'] = $session_user;
  459. $where['status'] = 0;
  460. if ($is_latest) {
  461. $where['createtime'] = ['>', time() - 10];
  462. }
  463. $new_msg = Db::name('fastchat_record')
  464. ->where($where)
  465. ->value('id');
  466. if ($new_msg) {
  467. $session_user_info = self::user_info($session_user);
  468. $new_msg = $session_user_info['nickname'] . ' 发来一条新消息!';
  469. break;
  470. }
  471. }
  472. return $new_msg;
  473. }
  474. /**
  475. * 获取会话的会话对象
  476. * @param array session 会话信息
  477. * @param string user_id 带标识符的当前用户id
  478. * @return string
  479. */
  480. public static function session_user($session, $oneself = NULL)
  481. {
  482. if (!$oneself) {
  483. $oneself = $_SESSION['user_id'];
  484. }
  485. if ($oneself == $session['user_id']) {
  486. switch ($session['session_type']) {
  487. case '0':
  488. $session_user = $session['session_user_id'] . '||user';
  489. break;
  490. case '1':
  491. $session_user = $session['session_user_id'] . '||admin';
  492. break;
  493. case '2':
  494. $session_user = $session['session_user_id'] . '||tourists';
  495. break;
  496. case '5':
  497. $session_user = $session['session_user_id'] . '||service_user';
  498. break;
  499. case '6':
  500. $session_user = $session['session_user_id'] . '||massager';
  501. break;
  502. default:
  503. $session_user = $session['session_user_id'] . '||none';
  504. break;
  505. }
  506. } else {
  507. $session_user = $session['user_id'];
  508. }
  509. return $session_user;
  510. }
  511. /**
  512. * 处理前台搜索框的提示文字
  513. * @param string search_type 配置信息
  514. * @return string
  515. */
  516. public static function search_tip_fill($search_type)
  517. {
  518. $search_tip = '';
  519. if ($search_type && strstr($search_type, 'none') === false) {
  520. $search_type = explode(',', $search_type);
  521. foreach ($search_type as $key => $value) {
  522. switch ($value) {
  523. case "username":
  524. $search_tip .= '用户名/';
  525. break;
  526. case "usermobile":
  527. $search_tip .= '手机号/';
  528. break;
  529. case "userid":
  530. $search_tip .= '用户ID/';
  531. break;
  532. case "nickname":
  533. $search_tip .= '用户昵称/';
  534. break;
  535. default:
  536. $search_tip .= '未知/';
  537. break;
  538. }
  539. }
  540. }
  541. if (!$search_tip) {
  542. $search_tip = '搜索其实很简单';
  543. } else {
  544. $search_tip = '搜索' . trim($search_tip, '/') . '发起会话';
  545. }
  546. return $search_tip;
  547. }
  548. /*
  549. * 检查/过滤变量
  550. */
  551. public static function check_variable(&$variable)
  552. {
  553. $variable = htmlspecialchars($variable);
  554. $variable = stripslashes($variable); // 删除反斜杠
  555. $variable = addslashes($variable); // 转义特殊符号
  556. $variable = trim($variable); // 去除字符两边的空格
  557. }
  558. }