Session.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. <?php
  2. /**
  3. * 会话信息
  4. * @author benzhan
  5. */
  6. class Session extends Model {
  7. protected $tableName = 'session';
  8. protected $dbKey = 'dw_chat';
  9. const MSG_TYPE_TEXT = 0;
  10. const MSG_TYPE_IMAGE = 1;
  11. const MSG_TYPE_VIDEO = 2;
  12. const MSG_TYPE_AUDIO = 3;
  13. const MSG_TYPE_REDPACK = 4;
  14. const LAST_MSG_NUM_HASH = 'globals:last_msg_num_hash';
  15. const LAST_MSG_HASH = 'globals:last_msg_hash';
  16. /**
  17. * 填充会话列表
  18. * @param string $user_id 用户id
  19. * @param array $list 会话列表
  20. * @param int $currentGroupId 当前群id
  21. *
  22. * @return mixed
  23. */
  24. public function fillSession($user_id, $list, $currentGroupId) {
  25. $group_ids = [];
  26. $user_ids = [];
  27. foreach ($list as $item) {
  28. if ($item['is_group']) {
  29. $group_ids[] = $item['session_id'];
  30. } else {
  31. // 把自己的用户名换掉
  32. $user_ids[] = str_replace([$user_id, '-'], ['', ''], $item['session_id']);
  33. }
  34. }
  35. $objUserInfo = new TableHelper('user_info', 'dw_chat');
  36. $_field = 'user_id, user_name, nick_name, cover_photo';
  37. $users = $objUserInfo->getAll(['user_id' => $user_ids], compact('_field'));
  38. $users = arrayFormatKey($users, 'user_id');
  39. $objGroupInfo = new TableHelper('group_info', 'dw_chat');
  40. $_field = 'group_id, group_name, group_title, cover_photo';
  41. $groups = $objGroupInfo->getAll(['group_id' => $group_ids], compact('_field'));
  42. $groups = arrayFormatKey($groups, 'group_id');
  43. $userGroups = [];
  44. if ($currentGroupId) {
  45. $objUserGroup = new UserGroup();
  46. $userGroups = $objUserGroup->objTable->getAll(['user_id' => $user_ids, 'group_id' => $currentGroupId], ['_field' => 'user_id, group_id']);
  47. $userGroups = arrayFormatKey($userGroups, 'user_id', 'group_id');
  48. }
  49. $objRedis = dwRedis::init(Eos::REDIS_SERV);
  50. foreach ($list as $i => $item) {
  51. if ($item['is_group']) {
  52. $group = $groups[$item['session_id']];
  53. $item['name'] = $group['group_title'] ?: $group['group_name'];
  54. $item['cover_photo'] = $group['cover_photo'];
  55. } else {
  56. // 把自己的用户名换掉
  57. $key = str_replace([$user_id, '-'], ['', ''], $item['session_id']);
  58. $user = $users[$key];
  59. $item['name'] = $user['nick_name'];
  60. $item['cover_photo'] = $user['cover_photo'];
  61. $item['in_group'] = $userGroups[$key] ? 1 : 0; // 已经在群
  62. }
  63. $lastNum = self::getLastMsgNum($item['session_id']);
  64. $item['unread'] = max($lastNum - $item['read_num'], 0);
  65. $item['cover_photo'] = coverReplaceImage($item['cover_photo']);
  66. $item['last_msg'] = self::getLastMsg($item['session_id'], $objRedis);
  67. $list[$i] = $item;
  68. }
  69. return $list;
  70. }
  71. public function getMsgList($session_id, $read_hash, $load_type, $is_group) {
  72. $where2 = [];
  73. if ($is_group) {
  74. $_field = '`hash`, `from`, `msg`, `msg_type`, `state`, create_time_int, msg_num';
  75. $objMsg = new TableHelper('group_msg', 'dw_chat');
  76. $where2['group_id'] = $session_id;
  77. } else {
  78. $_field = '`hash`, `from`, `to`, `msg`, `msg_type`, `state`, create_time_int, msg_num';
  79. $objMsg = new TableHelper('person_msg', 'dw_chat');
  80. $where2['session_id'] = $session_id;
  81. }
  82. if ($read_hash) {
  83. $currentRow = $objMsg->getRow(['hash' => $read_hash], compact('_field'));
  84. $create_time_int = (int) $currentRow['create_time_int'];
  85. } else {
  86. $create_time_int = 0;
  87. }
  88. $_limit = 100;
  89. if ($load_type == 0) {
  90. $create_time_int && $_where = "create_time_int > {$create_time_int}";
  91. $_sortKey = "create_time_int DESC";
  92. $keyWord2 = compact('_where', '_sortKey', '_limit', '_field');
  93. $list = $objMsg->getAll($where2, $keyWord2);
  94. } else {
  95. // 加载历史记录
  96. $_where = "create_time_int < {$create_time_int}";
  97. $_sortKey = "create_time_int DESC";
  98. $keyWord3 = compact('_where', '_sortKey', '_limit', '_field');
  99. $list = $objMsg->getAll($where2, $keyWord3);
  100. }
  101. $list = array_reverse($list);
  102. $list = $this->appendExtInfo($list);
  103. $user_ids = array_column($list, 'from');
  104. if (!$is_group) {
  105. $to_ids = array_column($list, 'to');
  106. $user_ids = array_merge($user_ids, $to_ids);
  107. }
  108. $user_ids = array_unique($user_ids);
  109. $userMap = $this->getUserMap($user_ids);
  110. return compact('userMap', 'list');
  111. }
  112. /**
  113. * 额外信息
  114. * @author solu
  115. * @param $list
  116. * @return mixed
  117. */
  118. private function appendExtInfo($list) {
  119. $userId = User::getUserId();
  120. $objRedpackLog = new RedpackLog();
  121. $trxIds = array_map(function($v) {
  122. $msg = Utils::decodeRC4($v['msg']);
  123. $data = json_decode($msg, true);
  124. return $data['trxId'];
  125. }, array_filter($list, function($v) {
  126. return $v['msg_type'] == self::MSG_TYPE_REDPACK;
  127. }));
  128. $redpack = [];
  129. if ($trxIds) {
  130. $objRedpack = new Redpack();
  131. $redpack = $objRedpack->objTable->getAll(['transfer_trx_id' => $trxIds], ['_field' => 'transfer_trx_id, status']);
  132. $redpack = arrayFormatKey($redpack, 'transfer_trx_id', 'status');
  133. }
  134. foreach ($list as $k => $v) {
  135. if ($v['msg_type'] == self::MSG_TYPE_REDPACK) {
  136. $msg = Utils::decodeRC4($v['msg']);
  137. $data = json_decode($msg, true);
  138. $trxId = $data['trxId'];
  139. $v['ext']['grabbed'] = $objRedpackLog->userGrabbed($userId, $trxId);
  140. $v['ext']['redpack_status'] = intval($redpack[$trxId]);
  141. }
  142. $list[$k] = $v;
  143. }
  144. return $list;
  145. }
  146. public function getUserMap($user_ids) {
  147. $objUserInfo = new TableHelper('user_info', 'dw_chat');
  148. $datas = $objUserInfo->getAll(['user_id' => $user_ids], ['_field' => 'user_id, user_name, nick_name, cover_photo']);
  149. coverReplaceArrImage($datas, 'cover_photo');
  150. return arrayFormatKey($datas, 'user_id');
  151. }
  152. /**
  153. * 修改状态
  154. * @param $session_id
  155. * @param $newData
  156. * @param $ext;
  157. */
  158. public function updateState($session_id, $newData, $ext = []) {
  159. $where = compact('session_id');
  160. $where = array_merge($where, $ext);
  161. $newData['update_time'] = NOW;
  162. $newData['update_time_int'] = microtime(true) * 1000;
  163. $this->objTable->updateObject($newData, $where);
  164. }
  165. /**
  166. * 检测私聊session
  167. * @author solu
  168. * @param $from
  169. * @param $sessionId
  170. * @return array
  171. * @throws Exception
  172. */
  173. public function checkPersonSession($from, $sessionId) {
  174. $uids = explode('-', $sessionId);
  175. $uids = array_filter($uids, function ($v) {return $v > 0;});
  176. // if (count($uids) != 2 || !$this->objTable->getRow(['user_id' => $from, 'session_id' => $sessionId])) {
  177. // throw new Exception('session_id error', CODE_PARAM_ERROR);
  178. // }
  179. if (!in_array($from, $uids)) {
  180. throw new Exception('user not in session', CODE_NO_PERMITION);
  181. }
  182. $to = 0;
  183. foreach ($uids as $uid) {
  184. if ($uid != $from) {
  185. $to = $uid;
  186. break;
  187. }
  188. }
  189. return [$from, $to];
  190. }
  191. /**
  192. * 检测群session
  193. * @author solu
  194. * @param $from
  195. * @param $sessionId
  196. * @return bool
  197. * @throws Exception
  198. */
  199. public function checkGroupSession($from, $sessionId) {
  200. if (!$this->objTable->getRow(['user_id' => $from, 'session_id' => $sessionId])) {
  201. throw new Exception('user not in session', CODE_NO_PERMITION);
  202. }
  203. return true;
  204. }
  205. public static function getPersonSessionId($from, $to) {
  206. if ($from > $to) {
  207. return "{$to}-{$from}";
  208. } else {
  209. return "{$from}-{$to}";
  210. }
  211. }
  212. private function initPersonSession($from, $to) {
  213. $session_id = self::getPersonSessionId($from, $to);
  214. $num = $this->objTable->getCount(compact('session_id'));
  215. if ($num < 2) { // 单方面删除会话的情况
  216. // 插入双方的session
  217. $datas = [[
  218. 'user_id' => $from,
  219. 'session_id' => $session_id,
  220. 'is_group' => 0,
  221. ], [
  222. 'user_id' => $to,
  223. 'session_id' => $session_id,
  224. 'is_group' => 0,
  225. ]
  226. ];
  227. // 第一次初始化,需要初始化
  228. $this->objTable->addObjectsIfNoExist($datas);
  229. }
  230. }
  231. /**
  232. * 发送私聊消息
  233. * @author solu
  234. * @param $from
  235. * @param $sessionId
  236. * @param $msg_type
  237. * @param $msg
  238. * @param $noEvent
  239. * @return array
  240. * @throws Exception
  241. */
  242. public function sendPersonMsg($from, $sessionId, $msg_type, $msg, $noEvent = false) {
  243. list($from, $to) = $this->checkPersonSession($from, $sessionId);
  244. $this->initPersonSession($from, $to);
  245. $t = self::getMS();
  246. 0 == $msg_type && $msg = htmlentities($msg);
  247. $lastNum = self::incrLastMsgNum($sessionId);
  248. $data = [
  249. 'session_id' => $sessionId,
  250. 'from' => intval($from),
  251. 'to' => intval($to),
  252. 'msg_type' => $msg_type,
  253. 'msg' => $msg,
  254. 'create_time' => NOW,
  255. 'create_time_int' => $t,
  256. 'msg_num' => $lastNum,
  257. ];
  258. $data['hash'] = self::_genHash($data);
  259. $objPersonMsg = new TableHelper('person_msg', 'dw_chat');
  260. if (!$objPersonMsg->addObject($data)) {
  261. throw new Exception('send message error', CODE_NORMAL_ERROR);
  262. }
  263. // 更新发送人已读序号
  264. $this->updateState($sessionId, ['read_num' => $lastNum], ['user_id' => $from]);
  265. $name = User::getUserNameById($from);
  266. $content = self::_msgHandle($msg, $msg_type);
  267. $eventData = [
  268. 'type' => 'msg',
  269. 'msg_type' => $msg_type,
  270. 'from' => $from,
  271. 'to' => strval($to),
  272. 'name' => $name,
  273. 'nick_name' => User::getUserNameById($from),
  274. 'content' => $content,
  275. 'hash' => $data['hash'],
  276. 'timestamp' => $t,
  277. ];
  278. !$noEvent && ThirdApi::pushPersonEvent($to, $eventData);
  279. self::setLastMsg($sessionId, $msg_type, $content, $from, $name);
  280. $this->updateState($sessionId, []);
  281. $eventData['content'] = $msg;
  282. return $eventData;
  283. }
  284. /**
  285. * 发送群聊消息
  286. * @author solu
  287. * @param $from
  288. * @param $groupId
  289. * @param $msg_type
  290. * @param $msg
  291. * @param $noEvent
  292. * @return array
  293. * @throws Exception
  294. */
  295. public function sendGroupMsg($from, $groupId, $msg_type, $msg, $noEvent = false) {
  296. $userMap = null;
  297. if (!$this->objTable->getRow(['user_id' => $from, 'session_id' => $groupId])) {
  298. // 聊天就自动加入群
  299. $objGroup = new GroupInfo();
  300. $objGroup->joinGroup($from, $groupId);
  301. // 第一次发言,需要返回用户信息
  302. $userMap = $this->getUserMap($from);
  303. } else if ((new UserGroup())->isBlock($groupId, $from)) {
  304. throw new Exception('Banned', CODE_NORMAL_ERROR);
  305. }
  306. $t = self::getMS();
  307. 0 == $msg_type && $msg = htmlentities($msg);
  308. $lastNum = self::incrLastMsgNum($groupId);
  309. $data = [
  310. 'group_id' => intval($groupId),
  311. 'from' => $from,
  312. 'msg_type' => $msg_type,
  313. 'msg' => $msg,
  314. 'create_time' => NOW,
  315. 'create_time_int' => $t,
  316. 'msg_num' => $lastNum,
  317. ];
  318. $data['hash'] = self::_genHash($data);
  319. $objGroupMsg = new TableHelper('group_msg', 'dw_chat');
  320. if (!$objGroupMsg->addObject($data)) {
  321. throw new Exception('send message error', CODE_NORMAL_ERROR);
  322. }
  323. // 更新发送人已读序号
  324. $this->updateState($groupId, ['read_num' => $lastNum], ['user_id' => $from]);
  325. $content = self::_msgHandle($msg, $msg_type);
  326. $name = GroupInfo::getGroupNameById($groupId);
  327. $eventData = [
  328. 'type' => 'msg',
  329. 'msg_type' => $msg_type,
  330. 'from' => $from,
  331. 'name' => $name,
  332. 'nick_name' => User::getUserNameById($from),
  333. 'content' => $content,
  334. 'hash' => $data['hash'],
  335. 'timestamp' => $t,
  336. ];
  337. !$noEvent && ThirdApi::pushGroupEvent($groupId, $eventData);
  338. self::setLastMsg($groupId, $msg_type, $content, $from, $name);
  339. $eventData['content'] = $msg;
  340. $eventData['userMap'] = $userMap;
  341. return $eventData;
  342. }
  343. private static function _genHash($data) {
  344. return md5(json_encode($data));
  345. }
  346. public static function _msgHandle($content, $msg_type, $len = 16) {
  347. if ($msg_type > 0) { // 只处理文本
  348. return $content;
  349. }
  350. $source = Utils::decodeRC4($content);
  351. !$source && $source = $content;
  352. if (mb_strlen($source) > $len) {
  353. $source = mb_substr($source, 0, $len);
  354. }
  355. return Utils::encodeRC4($source);
  356. }
  357. public static function getMS() {
  358. return intval(microtime(true) * 1000);
  359. }
  360. /**
  361. * 自增session num
  362. * @author solu
  363. * @param $sessionId
  364. * @return int
  365. */
  366. public static function incrLastMsgNum($sessionId) {
  367. $objRedis = dwRedis::init(Eos::REDIS_SERV);
  368. // 存在原子性问题
  369. if ($objRedis->hExists(self::LAST_MSG_NUM_HASH, $sessionId)) {
  370. return $objRedis->hIncrBy(self::LAST_MSG_NUM_HASH, $sessionId, 1);
  371. }
  372. if (is_numeric($sessionId)) { // 群组
  373. $objGroupMsg = new GroupMsg();
  374. $c = $objGroupMsg->objTable->getCount(['group_id' => $sessionId]);
  375. } else {
  376. $objPersonMsg = new PersonMsg();
  377. $c = $objPersonMsg->objTable->getCount(['session_id' => $sessionId]);
  378. }
  379. $c += 1;
  380. return $objRedis->hIncrBy(self::LAST_MSG_NUM_HASH, $sessionId, $c);
  381. }
  382. /**
  383. * 获取session最新num
  384. * @author solu
  385. * @param $sessionId
  386. * @param null $objRedis
  387. * @return int
  388. */
  389. public static function getLastMsgNum($sessionId, $objRedis = null) {
  390. !$objRedis && $objRedis = dwRedis::init(Eos::REDIS_SERV);
  391. return $objRedis->hGet(self::LAST_MSG_NUM_HASH, $sessionId) ?: 0;
  392. }
  393. public static function setLastMsg($sessionId, $msgType, $content, $from, $name) {
  394. $objRedis = dwRedis::init(Eos::REDIS_SERV);
  395. $msgType != self::MSG_TYPE_TEXT && $content = '';
  396. $data = [
  397. 'msg_type' => $msgType,
  398. 'content' => $content,
  399. 'from' => $from,
  400. 'name' => $name,
  401. 'nick_name' => User::getUserNameById($from),
  402. ];
  403. $objRedis->hSet(self::LAST_MSG_HASH, $sessionId, json_encode($data));
  404. }
  405. public static function getLastMsg($sessionId, $objRedis = null) {
  406. !$objRedis && $objRedis = dwRedis::init(Eos::REDIS_SERV);
  407. if ($objRedis->hExists(self::LAST_MSG_HASH, $sessionId)) {
  408. $json = $objRedis->hGet(self::LAST_MSG_HASH, $sessionId);
  409. return json_decode($json, true);
  410. }
  411. $isGroup = is_numeric($sessionId);
  412. if ($isGroup) { // 群组
  413. $objGroupMsg = new GroupMsg();
  414. $row = $objGroupMsg->objTable->getRow(['group_id' => $sessionId]);
  415. } else {
  416. $objPersonMsg = new PersonMsg();
  417. $row = $objPersonMsg->objTable->getRow(['session_id' => $sessionId]);
  418. }
  419. $data = null;
  420. if ($row) {
  421. $msg_type = $row['msg_type'];
  422. $content = '';
  423. $from = $row['from'];
  424. if ($msg_type == self::MSG_TYPE_TEXT) {
  425. $content = self::_msgHandle($row['msg'], $msg_type);
  426. }
  427. $nick_name = User::getUserNameById($from);
  428. $data = compact('msg_type', 'content', 'from', 'nick_name');
  429. $objRedis->hSet(self::LAST_MSG_HASH, $sessionId, json_encode($data));
  430. }
  431. return $data;
  432. }
  433. }