Im.php 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150
  1. <?php
  2. namespace app\enterprise\controller;
  3. use app\BaseController;
  4. use think\facade\Session;
  5. use think\facade\Db;
  6. use app\enterprise\model\{User, Message, GroupUser, Friend,Group,ChatDelog};
  7. use GatewayClient\Gateway;
  8. use Exception;
  9. use League\Flysystem\Util;
  10. use think\facade\Cache;
  11. use app\manage\model\{Config};
  12. use app\admin\model\GuessAskLanguages;
  13. use app\admin\model\QuestionLanguages;
  14. class Im extends BaseController
  15. {
  16. protected $fileType = ['file', 'image','video','voice','emoji'];
  17. /**
  18. * 猜你想问列表
  19. */
  20. function guessask()
  21. {
  22. try {
  23. $params = $this->request->param();
  24. $page = $params['page'] ?? 1;
  25. $limit = $params['limit'] ?? 15;
  26. $language_code = $this->lang;
  27. $query = GuessAskLanguages::where('language_code', $language_code)->where('status', 1);
  28. if (!empty($params['name'])) {
  29. $query = $query->where('name', 'like', '%'.$params['name'].'%');
  30. }
  31. if (isset($params['type']) && $params['type'] != '') {
  32. $query = $query->where('type', $params['type']);
  33. }
  34. $count = $query->count();
  35. $list = $query->order('is_top','desc')
  36. ->order('is_rec','desc')
  37. ->order('click_num','desc')
  38. ->limit($limit)
  39. ->page($page)
  40. ->select();
  41. } catch (Exception $e) {
  42. return error($e->getMessage());
  43. }
  44. return success('', [ 'count' => $count, 'list' => $list]);
  45. }
  46. /**
  47. * 猜你想问点击使用
  48. */
  49. function guessaskClick()
  50. {
  51. try {
  52. $id = $this->request->param('id');
  53. $info = GuessAskLanguages::where('id', $id)->where('status', 1)->find();
  54. if ($info) {
  55. $info->click_num = $info->click_num + 1;
  56. $info->save();
  57. }
  58. } catch (Exception $e) {
  59. return error($e->getMessage());
  60. }
  61. return success('');
  62. }
  63. /**
  64. * 问题列表
  65. */
  66. function question()
  67. {
  68. try {
  69. $params = $this->request->param();
  70. $page = $params['page'] ?? 1;
  71. $limit = $params['limit'] ?? 15;
  72. $language_code = $this->lang;
  73. $query = QuestionLanguages::where('language_code', $language_code)->where('status', 1);
  74. if (isset($params['question_type']) && $params['question_type'] != '') {
  75. $query = $query->where('question_type', $params['question_type']);
  76. }
  77. $count = $query->count();
  78. $list = $query->order('weight','desc')
  79. ->limit($limit)
  80. ->page($page)
  81. ->select();
  82. } catch (Exception $e) {
  83. return error($e->getMessage());
  84. }
  85. return success('', [ 'count' => $count, 'list' => $list]);
  86. }
  87. /**
  88. * 问题点赞、点否
  89. */
  90. function questionClick()
  91. {
  92. try {
  93. $id = $this->request->param('id');
  94. $is_like = $this->request->param('is_like');
  95. $question = QuestionLanguages::where('id', $id)->where('status', 1)->find();
  96. if ($question) {
  97. if ($is_like == 1) {
  98. $question->like_count = $question->like_count + 1;
  99. } else {
  100. $question->dislike_count = $question->dislike_count + 1;
  101. }
  102. $question->save();
  103. }
  104. } catch (Exception $e) {
  105. return error($e->getMessage());
  106. }
  107. return success('');
  108. }
  109. /**
  110. * 转人工客服
  111. */
  112. function transferHuman()
  113. {
  114. try {
  115. $user_id = $this->userInfo['user_id'];
  116. $user = User::where('user_id', $user_id)->find();
  117. if (!$user) {
  118. return error('用户不存在');
  119. }
  120. if ($user->role > 0) {
  121. return error('系统管理员无法转人工');
  122. }
  123. if ($user['service_status'] == -1) {
  124. $user->service_status = 0;
  125. $user->service->service_start = time();
  126. $user->save();
  127. //机器人客服
  128. $autoTask = Config::autoTask();
  129. $param = [
  130. 'id' => \utils\Str::getUuid(),
  131. 'type' => 'text',
  132. 'status' => 'going',
  133. 'sendTime' => time(),
  134. 'toContactId' => !empty($autoTask['user_id']) ? $autoTask['user_id'] : 1,
  135. 'content' => $this->globalConfig['transfer_to_human'],
  136. 'file_id' => 0,
  137. 'is_group' => 0
  138. ];
  139. Message::sendMsg($param, 0);
  140. }
  141. } catch (Exception $e) {
  142. return error($e->getMessage());
  143. }
  144. return success('');
  145. }
  146. // 获取联系人列表
  147. public function getContacts()
  148. {
  149. $data = User::getUserList([['status', '=', 1], ['user_id', '<>', $this->userInfo['user_id']]], $this->userInfo['user_id']);
  150. $count=Friend::where(['status'=>2,'friend_user_id'=>$this->uid])->count();
  151. $time=Friend::where(['friend_user_id'=>$this->uid,'is_invite'=>1])->order('create_time desc')->value('create_time');
  152. return success('', $data,$count,$time*1000);
  153. }
  154. // 获取聊天列表
  155. public function getChatList()
  156. {
  157. $data = User::getChatList($this->userInfo['user_id']);
  158. $count=Friend::where(['status'=>2,'friend_user_id'=>$this->uid])->count();
  159. $time=Friend::where(['friend_user_id'=>$this->uid,'is_invite'=>1])->order('create_time desc')->value('create_time');
  160. return success('', $data,$count,$time*1000);
  161. }
  162. // 获取好友列表
  163. public function getFriendList()
  164. {
  165. $data = User::getFriendList([['status', '=', 1], ['user_id', '<>', $this->userInfo['user_id']]],$this->userInfo['user_id']);
  166. return success('', $data);
  167. }
  168. // 获取群聊列表
  169. public function getGroupList()
  170. {
  171. $data = User::getGroupList($this->userInfo['user_id']);
  172. return success('', $data);
  173. }
  174. // 获取单个人员的信息
  175. public function getContactInfo(){
  176. $id = $this->request->param('id');
  177. $is_group = is_string($id) ? 1 : 0;
  178. $user=new User;
  179. $data=$user->setContact($id,$is_group);
  180. if(!$data){
  181. return warning($user->getError());
  182. }
  183. return success('',$data);
  184. }
  185. //发送消息
  186. public function sendMessage()
  187. {
  188. $param = $this->request->param();
  189. $param['user_id'] = $this->userInfo['user_id'];
  190. $message=new Message();
  191. $data = $message->sendMessage($param,$this->globalConfig);
  192. if ($data) {
  193. return success('', $data);
  194. } else {
  195. return warning($message->getError());
  196. }
  197. }
  198. //转发消息
  199. public function forwardMessage()
  200. {
  201. $param = $this->request->param();
  202. $userIds=$param['user_ids'] ?? [];
  203. if(!$userIds || count($userIds)>5){
  204. return warning(lang('im.forwardLimit',['count'=>5]));
  205. }
  206. $msg_id=$param['msg_id'] ?? 0;
  207. $message=Message::find($msg_id);
  208. if(!$message){
  209. return warning(lang('im.exist'));
  210. }
  211. $msg=$message->toArray();
  212. $userInfo=$this->userInfo;
  213. queuePush([
  214. 'action'=>'forwardMessage',
  215. 'message'=>$msg,
  216. 'user_ids'=>$userIds,
  217. 'config'=>$this->globalConfig,
  218. 'userInfo'=>$userInfo
  219. ]);
  220. return success(lang('im.forwardOk'));
  221. }
  222. // 获取用户信息
  223. public function getUserInfo()
  224. {
  225. $user_id = $this->request->param('user_id');
  226. $group_id = $this->request->param('group_id');
  227. $groupInfo=[];
  228. if($group_id){
  229. $group_id = explode('-', $group_id)[1];
  230. $groupInfo=Group::where(['group_id'=>$group_id])->find();
  231. if($groupInfo){
  232. // 查询操作对象的角色
  233. $groupInfo['userInfo']=GroupUser::where(['user_id'=>$user_id,'group_id'=>$group_id])->find() ?: [];
  234. // 查询我的角色
  235. $groupInfo['manageRole']=GroupUser::where(['user_id'=>$this->userInfo['user_id'],'group_id'=>$group_id])->value('role') ?: 3;
  236. }
  237. }
  238. $user=User::find($user_id);
  239. if(!$user){
  240. return error(lang('user.exist'));
  241. }
  242. $user->avatar=avatarUrl($user->avatar,$user->realname,$user->user_id,120);
  243. // 账号前面截取3位,后面截取两位,中间星号展示
  244. $user->account=substr($user->account, 0, 3).'******'.substr($user->account, -2, 2);
  245. // 查询好友关系
  246. $friend=Friend::where(['friend_user_id'=>$user_id,'create_user'=>$this->userInfo['user_id'],'status'=>1])->find();
  247. $user->friend=$friend ? : '';
  248. $location='';
  249. if($user->last_login_ip){
  250. $location=implode(" ", \Ip::find($user->last_login_ip));
  251. }
  252. $user->location=$location;
  253. $user->groupInfo=$groupInfo ? : [];
  254. $user->password='';
  255. $userModel=new User;
  256. $user->contactInfo=$userModel->setContact($user_id,0) ?? [];
  257. return success('', $user);
  258. }
  259. // 扫码登录,向客户端推送数据
  260. public function tokenLogin(){
  261. $param = $this->request->param();
  262. $token=$param['token'] ?? '';
  263. $client_id='';
  264. if($token){
  265. $client_id=authcode(urldecode($token),"DECODE", config('app.app_id'));
  266. if(!$client_id){
  267. return warning(lang('user.loginError'));
  268. }
  269. }else{
  270. return warning(lang('user.loginError'));
  271. }
  272. $userInfo=$this->userInfo;
  273. if(!$userInfo){
  274. return warning(lang('user.loginError'));
  275. }
  276. // 如果用户已经有设置
  277. $setting=$userInfo['setting'] ?: '';
  278. if($setting){
  279. $setting['hideMessageName']= $setting['hideMessageName']=='true' ? true : false;
  280. $setting['hideMessageTime']= $setting['hideMessageTime']=='true' ? true : false;
  281. $setting['avatarCricle']= $setting['avatarCricle']=='true' ? true : false;
  282. $setting['isVoice']= $setting['isVoice']=='true' ? true : false;
  283. $setting['sendKey']=(int)$setting['sendKey'];
  284. }
  285. $userInfo['setting']=$setting;
  286. Gateway::$registerAddress = config('gateway.registerAddress');
  287. //如果登录信息中含有client——id则自动进行绑定
  288. if($client_id){
  289. $user_id=$userInfo['user_id'];
  290. // 如果当前ID在线,将其他地方登陆挤兑下线
  291. if(Gateway::isUidOnline($user_id)){
  292. wsSendMsg($user_id,'offline',['id'=>$user_id,'client_id'=>$client_id,'isMobile'=>false]);
  293. }
  294. Gateway::bindUid($client_id, $user_id);
  295. // 查询团队,如果有团队则加入团队
  296. $group=Group::getMyGroup(['gu.user_id'=>$user_id,'gu.status'=>1]);
  297. if($group){
  298. $group=$group->toArray();
  299. $group_ids=arrayToString($group,'group_id',false);
  300. foreach($group_ids as $v){
  301. Gateway::joinGroup($client_id, $v);
  302. }
  303. }
  304. }
  305. $update=[
  306. 'last_login_time'=>time(),
  307. 'last_login_ip'=>$this->request->ip(),
  308. 'login_count'=>Db::raw('login_count+1')
  309. ];
  310. User::where('user_id',$userInfo['user_id'])->update($update);
  311. $authToken=User::refreshToken($userInfo,'web');
  312. $data=[
  313. 'sessionId'=>Session::getId(),
  314. 'authToken'=>$authToken,
  315. 'userInfo'=>$userInfo
  316. ];
  317. Gateway::sendToClient($client_id, json_encode(array(
  318. 'type' => 'tokenLogin',
  319. 'data' => $data,
  320. )));
  321. return success(lang('user.loginOk'),$data);
  322. }
  323. // 搜索用户
  324. public function searchUser(){
  325. $keywords=$this->request->param('keywords','');
  326. if(!$keywords){
  327. return success('',[]);
  328. }
  329. $map=['status'=>1,'account'=>$keywords];
  330. $list=User::where($map)->field(User::$defaultField)->where([['account','<>',$this->userInfo['account']]])->select()->toArray();
  331. if($list){
  332. $ids=array_column($list,'user_id');
  333. $friendList=Friend::getFriend([['create_user','=',$this->uid],['friend_user_id','in',$ids]]);
  334. foreach($list as $k=>$v){
  335. $list[$k]['avatar']=avatarUrl($v['avatar'],$v['realname'],$v['user_id'],120);
  336. $list[$k]['friend']=$friendList[$v['user_id']] ?? '';
  337. }
  338. }
  339. return success('', $list);
  340. }
  341. // 获取系统所有人员加搜索
  342. public function userList(){
  343. $keywords=$this->request->param('keywords','');
  344. $listRows=$this->request->param('limit',20);
  345. $page=$this->request->param('page',1);
  346. $map=['status'=>1];
  347. $field="user_id,realname,avatar";
  348. if(!$keywords){
  349. $list=User::where($map)->field($field)->order('user_id asc')->limit(20)->paginate(['list_rows'=>$listRows,'page'=>$page]);;
  350. if($list){
  351. $list=$list->toArray()['data'];
  352. }
  353. }else{
  354. $list=User::where($map)->field($field)->where([['account','<>',$this->userInfo['account']]])->whereLike('account|realname|name_py','%'.$keywords.'%')->select()->toArray();
  355. }
  356. if($list){
  357. foreach($list as $k=>$v){
  358. $list[$k]['avatar']=avatarUrl($v['avatar'],$v['realname'],$v['user_id'],120);
  359. $list[$k]['id']=$v['user_id'];
  360. }
  361. }
  362. return success('', $list);
  363. }
  364. // 获取聊天记录
  365. public function getMessageList()
  366. {
  367. $param = $this->request->param();
  368. $is_group = isset($param['is_group']) ? $param['is_group'] : 0;
  369. // 如果toContactId是数字,绝对是单聊
  370. $is_group = is_numeric($param['toContactId']) ? 0 : $is_group;
  371. // 设置当前聊天消息为已读
  372. $chat_identify = $this->setIsRead($is_group, $param['toContactId']);
  373. $type = isset($param['type']) ? $param['type'] : '';
  374. $is_at = isset($param['is_at']) ? $param['is_at'] : '';
  375. $map = ['chat_identify' => $chat_identify, 'status' => 1];
  376. $where = [];
  377. if ($type && $type != "all") {
  378. $map['type'] = $type;
  379. } else {
  380. if (isset($param['type'])) {
  381. $where[] = ['type', '<>', 'event'];
  382. }
  383. }
  384. $groupManage=[];
  385. // 群聊查询入群时间以后的消息
  386. if($is_group==1){
  387. $group_id = explode('-', $param['toContactId'])[1];
  388. $group=Group::where(['group_id'=> $group_id])->find();
  389. $groupManage=GroupUser::getGroupManage($group_id);
  390. if($group && $group['setting']){
  391. $groupSetting=json_decode($group['setting'],true);
  392. $history=$groupSetting['history'] ?? false;
  393. // 如果开启了历史记录才可以查看所有记录,否者根据进群时间查询记录
  394. if(!$history){
  395. $createTime=GroupUser::where(['group_id'=> $group_id,'user_id'=>$this->userInfo['user_id']])->value('create_time');
  396. $where[] = ['create_time', '>=', $createTime ? : 0];
  397. }
  398. }
  399. }
  400. $keywords = isset($param['keywords']) ? $param['keywords'] : '';
  401. if ($keywords && in_array($type, ['text', 'all'])) {
  402. $where[] = ['content', 'like', '%' . $keywords . '%'];
  403. }
  404. // 如果是查询@数据
  405. if($is_at){
  406. $atList=Db::name('message')->where($map)->where($where)->whereFindInSet('at',$this->userInfo['user_id'])->order('msg_id desc')->select()->toArray();
  407. if($atList){
  408. $data = $this->recombileMsg($atList,false);
  409. Message::setAtread($data,$this->userInfo['user_id']);
  410. return success('', $data, count($data));
  411. }else{
  412. return success('', [], 0);
  413. }
  414. }
  415. $listRows = $param['limit'] ?: 20;
  416. $pageSize = $param['page'] ?: 1;
  417. $last_id = $param['last_id'] ?? 0;
  418. if($last_id){
  419. $where[]=['msg_id','<',$last_id];
  420. $pageSize=1;
  421. }
  422. //判断是客服还是用户
  423. if ($this->userInfo['role'] == 0) {
  424. //用户可见历史记录的时长
  425. $hours = Config::where('field', 'user_show_message')->value('val');
  426. if ($hours > 0) {
  427. $start_time = time() - $hours * 60 * 60;
  428. $where[] = ['create_time', '>=', $start_time];
  429. }
  430. } elseif ($this->userInfo['role'] == 3) {
  431. //客服可见历史记录的时长
  432. $hours = Config::where('field', 'kefu_show_message')->value('val');
  433. if ($hours > 0) {
  434. $start_time = time() - $hours * 60 * 60;
  435. $where[] = ['create_time', '>=', $start_time];
  436. }
  437. }
  438. $list = Message::getList($map, $where, 'msg_id desc', $listRows, $pageSize);
  439. $data = $this->recombileMsg($list,true,$groupManage);
  440. // 如果是群聊并且是第一页消息,需要推送@数据给用户
  441. if($param['is_group']==1 && $param['page']==1){
  442. $isPush=Cache::get('atMsgPush'.$chat_identify) ?? '';
  443. $atList=Db::name('message')->where(['chat_identify'=>$chat_identify,'is_group'=>1])->whereFindInSet('at',$this->userInfo['user_id'])->order('msg_id desc')->select()->toArray();
  444. $msgIda=array_column($atList,'msg_id');
  445. // 如果两次推送at数据的列表不一样,则推送
  446. if($isPush!=json_encode($msgIda)){
  447. $atData=$this->recombileMsg($atList,false);
  448. wsSendMsg($this->userInfo['user_id'],'atMsgList',[
  449. 'list'=>$atData,
  450. 'count'=>count($atData),
  451. 'toContactId'=>$param['toContactId']
  452. ]);
  453. Cache::set('atMsgPush'.$chat_identify,json_encode($msgIda),60);
  454. }
  455. }
  456. // 如果是消息管理器则不用倒序
  457. if (!isset($param['type'])) {
  458. $data = array_reverse($data);
  459. }
  460. return success('', $data, $list->total());
  461. }
  462. // 获取单条消息详情
  463. public function getMessageInfo()
  464. {
  465. $param = $this->request->param();
  466. $id = $param['msg_id'] ?? 0;
  467. $message = Message::where(['msg_id' => $id])->find();
  468. if ($message) {
  469. $data = $this->recombileMsg([$message], false);
  470. return success('', $data);
  471. } else {
  472. return warning(lang('im.exist'));
  473. }
  474. }
  475. // 获取单条消息上下文
  476. public function getMessageContext()
  477. {
  478. $param = $this->request->param();
  479. $is_group = isset($param['is_group']) ? $param['is_group'] : 0;
  480. $id = $param['msg_id'] ?? 0;
  481. $direction = $param['direction'] ?? 0;
  482. $message = Message::where(['msg_id' => $id])->find();
  483. if (!$message) {
  484. return warning(lang('im.exist'));
  485. }
  486. $groupManage=[];
  487. $where = [];
  488. $map = ['chat_identify' => $message['chat_identify'], 'status' => 1];
  489. if($is_group==1 && $direction<2){
  490. $group_id = $message['to_user'];
  491. $group=Group::where(['group_id'=> $group_id])->find();
  492. $groupManage=GroupUser::getGroupManage($group_id);
  493. if($group && $group['setting']){
  494. $groupSetting=json_decode($group['setting'],true);
  495. $history=$groupSetting['history'] ?? false;
  496. // 如果开启了历史记录才可以查看所有记录,否者根据进群时间查询记录
  497. if(!$history){
  498. $createTime=GroupUser::where(['group_id'=> $group_id,'user_id'=>$this->userInfo['user_id']])->value('create_time');
  499. $where[] = ['create_time', '>=', $createTime ? : 0];
  500. }
  501. }
  502. }
  503. if($direction==0){
  504. $where[] = ['msg_id', '<', $id];
  505. $beforeList = Message::where($map)->where($where)->order('msg_id desc')->limit(5)->select()->toArray();
  506. $beforeList = array_reverse($beforeList);
  507. $where2 = [];
  508. $where2[] = ['msg_id', '>=', $id];
  509. $afterList = Message::where($map)->where($where2)->order('msg_id asc')->limit(5)->select()->toArray();
  510. $data = array_merge($beforeList, $afterList);
  511. }elseif($direction==1){
  512. $where[] = ['msg_id', '<', $id];
  513. $data = Message::where($map)->where($where)->order('msg_id desc')->limit(5)->select()->toArray();
  514. $data = array_reverse($data);
  515. }else{
  516. $where[] = ['msg_id', '>', $id];
  517. $data = Message::where($map)->where($where)->order('msg_id asc')->limit(5)->select()->toArray();
  518. }
  519. if($data){
  520. $data = $this->recombileMsg($data, false,$groupManage);
  521. }
  522. return success('', $data);
  523. }
  524. protected function recombileMsg($list,$isPagination=true,$manage=[])
  525. {
  526. $data = [];
  527. $userInfo = $this->userInfo;
  528. if ($list) {
  529. $listData = $isPagination ? $list->toArray()['data'] : $list;
  530. $userList = User::matchUser($listData, true, 'from_user', 120);
  531. foreach ($listData as $k => $v) {
  532. // 屏蔽已删除的消息
  533. if ($v['del_user']) {
  534. $delUser = explode(',', $v['del_user']);
  535. if (in_array($userInfo['user_id'], $delUser)) {
  536. unset($list[$k]);
  537. continue;
  538. // $v['type']="event";
  539. // $v['content']="删除了一条消息";
  540. }
  541. }
  542. $content = str_encipher($v['content'],false);
  543. $preview = '';
  544. $ext='';
  545. if (in_array($v['type'], $this->fileType)) {
  546. $content = getFileUrl($content);
  547. $preview = previewUrl($content);
  548. $ext=getExtUrl($content);
  549. }
  550. $fromUser = $userList[$v['from_user']];
  551. // 处理撤回的消息
  552. if ($v['type'] == "event" && $v['is_undo']==1) {
  553. if ($v['from_user'] == $userInfo['user_id']) {
  554. $content = lang('im.you'). $content;
  555. } elseif ($v['is_group'] == 1) {
  556. $content = $fromUser['realname'] . $content;
  557. } else {
  558. $content = lang('im.other') . $content;
  559. }
  560. }
  561. $toContactId=$v['is_group'] ==1 ? 'group-'.$v['to_user'] : $v['to_user'];
  562. $atList=($v['at'] ?? null) ? explode(',',$v['at']): [];
  563. $role=$manage[$v['from_user']] ?? 3;
  564. $data[] = [
  565. 'msg_id' => $v['msg_id'],
  566. 'id' => $v['id'],
  567. 'status' => "succeed",
  568. 'type' => $v['type'],
  569. 'sendTime' => (is_string($v['create_time']) ? strtotime($v['create_time']) : $v['create_time'])* 1000,
  570. 'content' => $content,
  571. 'preview' => $preview,
  572. 'download' => $v['file_id'] ? getMainHost().'/filedown/'.encryptIds($v['file_id']) : '',
  573. 'is_read' => $v['is_read'],
  574. 'is_group' => $v['is_group'],
  575. 'at' => $atList,
  576. 'toContactId' => $toContactId,
  577. 'from_user' => $v['from_user'],
  578. 'file_id' => $v['file_id'],
  579. 'file_cate' => $v['file_cate'],
  580. 'fileName' => $v['file_name'],
  581. 'fileSize' => $v['file_size'],
  582. 'fromUser' => $fromUser,
  583. 'extUrl'=>$ext,
  584. 'role'=>$role,
  585. 'extends'=>is_string($v['extends'])?json_decode($v['extends'],true) : $v['extends']
  586. ];
  587. }
  588. }
  589. return $data;
  590. }
  591. // 设置当前窗口的消息默认为已读
  592. public function setMsgIsRead()
  593. {
  594. $param = $this->request->param();
  595. // 判断是否是一个二维数组
  596. if (is_array($param['messages'][0] ?? '')) {
  597. $messages=$param['messages'];
  598. } else {
  599. $messages=[$param['messages']];
  600. }
  601. $this->setIsRead($param['is_group'], $param['toContactId'],$messages);
  602. if (!$param['is_group']) {
  603. wsSendMsg($param['fromUser'], 'isRead', $messages, 0);
  604. }
  605. return success('');
  606. }
  607. // 设置全部为已读
  608. public function readAllMsg()
  609. {
  610. // 阅读所有单聊
  611. $map = ['to_user' => $this->userInfo['user_id'], 'is_read' => 0, 'is_group' => 0];
  612. Message::where($map)->update(['is_read' => 1]);
  613. // 阅读所有群聊
  614. GroupUser::where(['user_id' => $this->userInfo['user_id'], 'status' => 1])->update(['unread'=>0]);
  615. return success('');
  616. }
  617. // 设置消息已读
  618. protected function setIsRead($is_group, $to_user,$messages=[])
  619. {
  620. if ($is_group==1) {
  621. $chat_identify = $to_user;
  622. } else if($is_group==0) {
  623. $chat_identify = chat_identify($this->userInfo['user_id'], $to_user);
  624. } else if($is_group==2){
  625. $chat_identify = $to_user;
  626. }
  627. $data=[
  628. 'action'=>'setIsRead',
  629. 'is_group'=>$is_group,
  630. 'to_user'=>$to_user,
  631. 'messages'=>$messages,
  632. 'user_id'=>$this->userInfo['user_id']
  633. ];
  634. queuePush($data,3);
  635. return $chat_identify;
  636. }
  637. // 聊天设置
  638. public function setting()
  639. {
  640. $param = $this->request->param();
  641. if ($param) {
  642. User::where(['user_id' => $this->userInfo['user_id']])->update(['setting' => $param]);
  643. return success('');
  644. }
  645. return warning('');
  646. }
  647. // 撤回消息
  648. public function undoMessage()
  649. {
  650. $param = $this->request->param();
  651. $id = $param['id'];
  652. $message = Message::where(['id' => $id])->find();
  653. if ($message) {
  654. // 如果时间超过了2分钟也不能撤回
  655. $createTime=is_string($message['create_time']) ? strtotime($message['create_time']) : $message['create_time'];
  656. $redoTime=$this->globalConfig['chatInfo']['redoTime'] ?? 120;
  657. if ($this->userInfo['role'] == 0 && $this->globalConfig['user_cancel_message'] && $this->globalConfig['user_cancel_message']) {
  658. $redoTime=$this->globalConfig['user_cancel_message_time'] * 60;
  659. } elseif ($this->userInfo['role'] != 0 && $this->globalConfig['kefu_cancel_message']) {
  660. $redoTime=$this->globalConfig['kefu_cancel_message'] * 60;
  661. }
  662. if(time()-$createTime>$redoTime && $message['is_group']==0){
  663. return warning(lang('im.redoLimitTime',['time'=>floor($redoTime/60)]));
  664. }
  665. $text = lang('im.redo');
  666. $fromUserName = lang('im.other');
  667. $toContactId = $message['to_user'];
  668. if ($message['is_group'] == 1) {
  669. $fromUserName = $this->userInfo['realname'];
  670. $toContactId = explode('-', $message['chat_identify'])[1];
  671. // 如果是群聊消息撤回,需要判断是否是群主或者管理员,如果是则可以撤回
  672. if($message['from_user']!=$this->userInfo['user_id']){
  673. $groupUser=GroupUser::where(['user_id'=>$this->userInfo['user_id'],'group_id'=>$toContactId])->find();
  674. if(!$groupUser || !in_array($groupUser['role'],[1,2])){
  675. return warning(lang('system.notAuth'));
  676. }
  677. $text=lang('im.manageRedo');
  678. }
  679. }
  680. $message->content = str_encipher($text);
  681. $message->type = 'event';
  682. $message->is_undo = 1;
  683. //@的数据清空
  684. $message->at = '';
  685. $message->save();
  686. $info = $message->toArray();
  687. // $data = $info;
  688. $data['content'] = $fromUserName . $text;
  689. $data['sendTime'] = $createTime * 1000;
  690. $data['id'] = $info['id'];
  691. $data['from_user'] = $info['from_user'];
  692. $data['msg_id'] = $info['msg_id'];
  693. $data['status'] = $info['status'];
  694. $data['type'] = 'event';
  695. $data['is_last'] = $info['is_last'];
  696. $data['toContactId'] = $message['is_group'] == 1 ? $info['chat_identify'] : $toContactId;
  697. $data['isMobile'] = $this->request->isMobile() ? 1 : 0;
  698. wsSendMsg($toContactId, 'undoMessage', $data, $info['is_group']);
  699. if($info['is_group']==0){
  700. // 给自己也发一份推送,多端同步
  701. $data['content'] =lang('im.you'). $text;
  702. wsSendMsg($this->userInfo['user_id'], 'undoMessage', $data, $info['is_group']);
  703. }
  704. return success('');
  705. } else {
  706. return warning();
  707. }
  708. }
  709. // 删除消息
  710. public function removeMessage()
  711. {
  712. $param = $this->request->param();
  713. $id = $param['id'];
  714. $map = ['id' => $id];
  715. $message = Message::where($map)->find();
  716. if ($message) {
  717. $message->del_user = $this->userInfo['user_id'];
  718. if ($message['is_group'] == 1) {
  719. if ($message['del_user']) {
  720. $message->del_user .= ',' . $this->userInfo['user_id'];
  721. }
  722. } else {
  723. if ($message['del_user'] > 0) {
  724. $message->where($map)->delete();
  725. return success(lang('system.delOk'));
  726. }
  727. }
  728. $message->save();
  729. return success('');
  730. } else {
  731. return warning('');
  732. }
  733. }
  734. // 消息免打扰
  735. public function isNotice()
  736. {
  737. $param = $this->request->param();
  738. $user_id = $this->userInfo['user_id'];
  739. $id = $param['id'];
  740. if ($param['is_group'] == 1) {
  741. $group_id = explode('-', $param['id'])[1];
  742. GroupUser::update(['is_notice' => $param['is_notice']], ['user_id' => $user_id, 'group_id' => $group_id]);
  743. } else {
  744. $map = ['create_user' => $user_id, 'friend_user_id' => $id];
  745. $friend = Friend::where($map)->find();
  746. try {
  747. if ($friend) {
  748. $friend->is_notice = $param['is_notice'];
  749. $friend->save();
  750. } else {
  751. $info = [
  752. 'create_user' => $user_id,
  753. 'friend_user_id' => $id,
  754. 'is_notice' => $param['is_notice']
  755. ];
  756. Friend::create($info);
  757. }
  758. return success('');
  759. } catch (Exception $e) {
  760. return error($e->getMessage());
  761. }
  762. }
  763. wsSendMsg($user_id,"setIsNotice",['id'=>$id,'is_notice'=>$param['is_notice'],'is_group'=>$param['is_group']]);
  764. return success('');
  765. }
  766. // 设置聊天置顶
  767. public function setChatTop()
  768. {
  769. $param = $this->request->param();
  770. $user_id = $this->userInfo['user_id'];
  771. $is_group = $param['is_group'] ?: 0;
  772. $id = $param['id'];
  773. try {
  774. if ($is_group == 1) {
  775. $group_id = explode('-', $param['id'])[1];
  776. GroupUser::update(['is_top' => $param['is_top']], ['user_id' => $user_id, 'group_id' => $group_id]);
  777. } else {
  778. $map = ['create_user' => $user_id, 'friend_user_id' => $id];
  779. $friend = Friend::where($map)->find();
  780. if ($friend) {
  781. $friend->is_top = $param['is_top'];
  782. $friend->save();
  783. } else {
  784. $info = [
  785. 'create_user' => $user_id,
  786. 'friend_user_id' => $id,
  787. 'is_top' => $param['is_top']
  788. ];
  789. Friend::create($info);
  790. }
  791. }
  792. wsSendMsg($user_id,"setChatTop",['id'=>$id,'is_top'=>$param['is_top'],'is_group'=>$is_group]);
  793. return success('');
  794. } catch (Exception $e) {
  795. return error($e->getMessage());
  796. }
  797. }
  798. // 删除聊天
  799. public function delChat()
  800. {
  801. $param = $this->request->param();
  802. $user_id = $this->userInfo['user_id'];
  803. $is_group = $param['is_group'] ?: 0;
  804. $id = $param['id'];
  805. $data=[
  806. 'user_id'=>$user_id,
  807. 'is_group'=>$is_group,
  808. 'to_user'=>$id
  809. ];
  810. ChatDelog::create($data);
  811. ChatDelog::updateCache($user_id);
  812. return success('');
  813. }
  814. // 向用户发送消息
  815. public function sendToMsg(){
  816. $param=$this->request->param();
  817. $toContactId=$param['toContactId'];
  818. $type=$param['type'];
  819. $status=$param['status'];
  820. $event=$param['event'] ?? 'calling';
  821. if($event=='calling'){
  822. $status=3;
  823. }
  824. $sdp=$param['sdp'] ?? '';
  825. $iceCandidate=$param['iceCandidate'] ?? '';
  826. $callTime=$param['callTime'] ?? '';
  827. $msg_id=$param['msg_id'] ?? '';
  828. $id=$param['id'] ?? '';
  829. $code=($param['code'] ?? '') ?: 901;
  830. // 如果该用户不在线,则发送忙线
  831. Gateway::$registerAddress = config('gateway.registerAddress');
  832. if(!Gateway::isUidOnline($toContactId)){
  833. $toContactId=$this->userInfo['user_id'];
  834. $code=907;
  835. $event='busy';
  836. sleep(1);
  837. }
  838. switch($code){
  839. case 902:
  840. $content=lang('webRtc.cancel');
  841. break;
  842. case 903:
  843. $content=lang('webRtc.refuse');
  844. break;
  845. case 905:
  846. $content=lang('webRtc.notConnected');
  847. break;
  848. case 906:
  849. $content=lang('webRtc.duration',['time'=>date("i:s",$callTime)]);
  850. break;
  851. case 907:
  852. $content=lang('webRtc.busy');
  853. break;
  854. case 908:
  855. $content=lang('webRtc.other');
  856. break;
  857. default:
  858. $content=$type==1 ?lang('webRtc.video') : lang('webRtc.audio');
  859. break;
  860. }
  861. switch($event){
  862. case 'calling':
  863. $content=$type==1 ?lang('webRtc.video'): lang('webRtc.audio');
  864. break;
  865. case 'acceptRtc':
  866. $content=lang('webRtc.answer');
  867. break;
  868. case 'iceCandidate':
  869. $content=lang('webRtc.exchange');
  870. break;
  871. }
  872. $userInfo=$this->userInfo;
  873. $userInfo['id']=$userInfo['user_id'];
  874. $user = new User();
  875. $data=[
  876. 'id'=>$id,
  877. 'msg_id'=>$msg_id,
  878. 'sendTime'=>time()*1000,
  879. 'toContactId'=>$toContactId,
  880. 'content'=>$content,
  881. 'type'=>'webrtc',
  882. 'status'=>'succeed',
  883. 'is_group'=>0,
  884. 'is_read'=>0,
  885. 'fromUser'=>$userInfo,
  886. 'at'=>[],
  887. 'extends'=>[
  888. 'type'=>$type, //通话类型,1视频,0语音。
  889. 'status'=>$status, //,1拨打方,2接听方
  890. 'event'=>$event,
  891. 'callTime'=>$callTime,
  892. 'sdp'=>$sdp,
  893. 'code'=>$code, //通话状态:呼叫901,取消902,拒绝903,接听904,未接通905,接通后挂断906,忙线907,其他端操作908
  894. 'iceCandidate'=>$iceCandidate,
  895. 'isMobile'=>$this->request->isMobile() ? 1 : 0,
  896. ]
  897. ];
  898. if($event=='calling'){
  899. $chat_identify=chat_identify($userInfo['id'],$toContactId);
  900. $msg=[
  901. 'from_user'=>$userInfo['id'],
  902. 'to_user'=>$toContactId,
  903. 'id'=>$id,
  904. 'content'=>str_encipher($content),
  905. 'chat_identify'=>$chat_identify,
  906. 'create_time'=>time(),
  907. 'type'=>$data['type'],
  908. 'is_group'=>0,
  909. 'is_read'=>0,
  910. 'extends'=>$data['extends'],
  911. ];
  912. $message=new Message();
  913. $message->update(['is_last'=>0],['chat_identify'=>$chat_identify]);
  914. $message->save($msg);
  915. $msg_id=$message->msg_id;
  916. $data['msg_id']=$msg_id;
  917. // 将接收人设置为发送人才能定位到该消息
  918. $data['toContactId']=$userInfo['id'];
  919. $data['toUser']=$toContactId;
  920. }elseif($event=='hangup'){
  921. $message=Message::where(['id'=>$id])->find();
  922. if(!$message){
  923. return error(lang('webRtc.fail'));
  924. }
  925. if($message){
  926. $message->content=str_encipher($content);
  927. $extends=$message->extends;
  928. $extends['code']=$code;
  929. $extends['callTime']=$callTime;
  930. $message->extends=$extends;
  931. $message->save();
  932. }
  933. }
  934. wsSendMsg($toContactId,'webrtc',$data);
  935. $wsData=$data;
  936. if(in_array($event,['calling','acceptRtc','hangup'])){
  937. if(in_array($event,['acceptRtc','hangup'])){
  938. $data['extends']['event']='otherOpt'; //其他端操作
  939. }
  940. $data['toContactId']=$toContactId;
  941. $data['contactInfo']=$user->setContact($toContactId,0,'webrtc',$content) ? : [];
  942. wsSendMsg($userInfo['id'],'webrtc',$data);
  943. }
  944. return success('',$wsData);
  945. }
  946. // 修改密码
  947. public function editPassword()
  948. {
  949. if(env('app.demon_mode',false)){
  950. return warning(lang('system.demoMode'));
  951. }
  952. $user_id = $this->userInfo['user_id'];
  953. $user=User::find($user_id);
  954. if(!$user){
  955. return warning(lang('user.exist'));
  956. }
  957. $account=$user->account;
  958. $code=$this->request->param('code','');
  959. $originalPassword = $this->request->param('originalPassword', '');
  960. if($code){
  961. if(Cache::get($account)!=$code){
  962. return warning(lang('user.codeErr'));
  963. }
  964. }elseif($originalPassword){
  965. if(password_hash_tp($originalPassword,$user->salt)!= $user->password){
  966. return warning(lang('user.passErr'));
  967. }
  968. }else{
  969. return warning(lang('system.parameterError'));
  970. }
  971. try{
  972. $password = $this->request->param('password','');
  973. if($password){
  974. $salt=$user->salt;
  975. $user->password= password_hash_tp($password,$salt);
  976. }
  977. $user->save();
  978. return success(lang('system.editOk'));
  979. }catch (\Exception $e){
  980. return error(lang('system.editFail'));
  981. }
  982. }
  983. // 修改用户信息
  984. public function updateUserInfo(){
  985. try{
  986. $data = $this->request->param();
  987. $user=User::find($this->uid);
  988. if(!$user){
  989. return warning(lang('user.exist'));
  990. }
  991. // 接入用户名检测服务
  992. event('GreenText',['content'=>$data['realname'],'service'=>"nickname_detection"]);
  993. // 个性签名检测服务
  994. event('GreenText',['content'=>$data['motto'],'service'=>"comment_detection"]);
  995. $user->realname =$data['realname'];
  996. $user->email =$data['email'];
  997. $user->motto=$data['motto'];
  998. $user->sex =$data['sex'];
  999. $user->name_py= pinyin_sentence($data['realname']);
  1000. $user->save();
  1001. return success(lang('system.editOk'), $data);
  1002. }catch (\Exception $e){
  1003. return error($e->getMessage());
  1004. }
  1005. }
  1006. // 修改账户
  1007. public function editAccount(){
  1008. if(env('app.demon_mode',false)){
  1009. return warning(lang('system.demoMode'));
  1010. }
  1011. $code=$this->request->param('code','');
  1012. $newCode=$this->request->param('newCode','');
  1013. $account=$this->request->param('account','');
  1014. $isUser=User::where('account',$account)->find();
  1015. if($isUser){
  1016. return warning(lang('user.already'));
  1017. }
  1018. $user=User::find($this->uid);
  1019. if(!$user){
  1020. return warning(lang('user.exist'));
  1021. }
  1022. // 如果已经认证过了,则需要验证验证码
  1023. if($user->is_auth){
  1024. if(Cache::get($user->account)!=$code){
  1025. return warning(lang('user.codeErr'));
  1026. }
  1027. }
  1028. if(Cache::get($account)!=$newCode){
  1029. return warning(lang('user.newCodeErr'));
  1030. }
  1031. try{
  1032. $user->account=$account;
  1033. $user->is_auth=1;
  1034. $user->save();
  1035. return success(lang('system.editOk'));
  1036. }catch (\Exception $e){
  1037. return error(lang('system.editFail'));
  1038. }
  1039. }
  1040. // 阅读@消息
  1041. public function readAtMsg(){
  1042. $param = $this->request->param();
  1043. $atList=Db::name('message')->where(['chat_identify'=>$param['toContactId'],'is_group'=>1])->whereFindInSet('at',$this->userInfo['user_id'])->order('msg_id desc')->select();
  1044. $atData=$this->recombileMsg($atList,false);
  1045. Message::setAtRead($atData,$this->userInfo['user_id']);
  1046. // $message=Message::where('msg_id',$param['msg_id'])->select();
  1047. // $atList=($message ?? null) ? explode(',',$message): [];
  1048. // // 两个数组取差集
  1049. // $newAtList = array_diff($atList, [$this->userInfo['user_id']]);
  1050. // Message::where('msg_id',$param['msg_id'])->update(['at'=>implode(',',$newAtList)]);
  1051. return success('');
  1052. }
  1053. // 获取系统公告
  1054. public function getAdminNotice(){
  1055. $data=Message::where(['chat_identify'=>'admin_notice'])->order('msg_id desc')->find();
  1056. $extends=$data['extends'] ?? [];
  1057. if(!$extends){
  1058. $extends['title']='';
  1059. }
  1060. $createTime=$data['create_time'] ?? 0;
  1061. if(!$createTime){
  1062. $extends['create_time']=$createTime;
  1063. }else{
  1064. $extends['create_time']=is_string($data['create_time']) ? strtotime($data['create_time']) : $data['create_time'];
  1065. }
  1066. return success('',$extends);
  1067. }
  1068. // 双向删除消息
  1069. public function delMessage(){
  1070. $param = $this->request->param();
  1071. $id = $param['id'];
  1072. if(!$this->globalConfig['chatInfo']['dbDelMsg']){
  1073. return warning(lang('system.notAuth'));
  1074. }
  1075. $message = Message::where(['id' => $id])->find();
  1076. if ($message) {
  1077. if($message['from_user']!=$this->userInfo['user_id']){
  1078. return warning(lang('system.notAuth'));
  1079. }
  1080. Message::where(['id' => $id])->delete();
  1081. // 如果是最后一条消息,需要将上一条设置为最后一条
  1082. if($message['is_last']){
  1083. Message::where(['chat_identify'=>$message['chat_identify']])->order('msg_id desc')->limit(1)->update(['is_last'=>1]);
  1084. }
  1085. $toContactId = $message['to_user'];
  1086. if ($message['is_group'] == 1) {
  1087. $toContactId = explode('-', $message['chat_identify'])[1];
  1088. }
  1089. wsSendMsg($toContactId, 'delMessage', $message, $message['is_group']);
  1090. return success('');
  1091. } else {
  1092. return warning(lang('im.exist'));
  1093. }
  1094. }
  1095. }