ChatController.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <?php
  2. namespace backend\controllers;
  3. use Yii;
  4. use yii\helpers\Json;
  5. use common\components\weixinUtil;
  6. use common\components\qqFaceUtil;
  7. use common\components\stringUtil;
  8. use common\components\util;
  9. use common\services\xhUserTagClassService;
  10. use common\services\xhUserService;
  11. use common\services\xhTMessageService;
  12. use common\components\configDict;
  13. use common\components\chuanglanSMS;
  14. class ChatController extends BackendController
  15. {
  16. const CUSTOMER = 'customer';
  17. const MERCHANT = 'merchant';
  18. /**
  19. * 商家访问的聊天页
  20. * @return string
  21. */
  22. public function actionIndex()
  23. {
  24. $get = Yii::$app->request->get();
  25. $customId = isset($get['customId']) ? $get['customId'] : 0;
  26. $custom = xhUserService::getById($customId);
  27. $merchantId = $this->merchantId;
  28. $adminId = $this->adminId;
  29. $merchant = $this->merchant;
  30. if(empty($custom)){
  31. util::stop("没有用户信息哦");
  32. }
  33. if(isset($custom['merchantId']) == false || $custom['merchantId'] != $merchantId){
  34. util::stop("非法用户");
  35. }
  36. $chatLogKey = "merchant_history_{$merchantId}_{$customId}";
  37. $themself = ['avatar' => $custom['avatar'],'userName' => $custom['userName']];
  38. $ourself = ['uid'=>$merchantId, 'chatUid'=>$customId, 'avatar' => $merchant['logo'],'userName' => $merchant['merchantName'], 'chatLogKey' => $chatLogKey, 'role'=>'merchant'];
  39. $emojiCode = qqFaceUtil::getEmojiCode();
  40. $hasHistoryPage = isset($get['hasHistoryPage']) ? $get['hasHistoryPage'] : '';
  41. $userTagStr = isset($custom['tag']) ? $custom['tag'] : '';
  42. $userTag = explode(',',trim($userTagStr));
  43. $merchantTag = xhUserTagClassService::getMerchantTag($merchantId);
  44. //swoole:客户是否在线
  45. $clientKey = self::CUSTOMER . ':customer_'.$customId.':merchant_' . $merchantId;
  46. /*$clientId = Yii::$app->redis->executeCommand('GET', [$clientKey]);
  47. $clientId = empty($clientId) ? 0 : $clientId;*/
  48. $existClientId = Yii::$app->redis->executeCommand('SCARD', [$clientKey]);
  49. $clientId = empty($existClientId) ? 0 : 1;
  50. $swooleUrl = substr($this->frontUrl, 7);
  51. $account = $merchant['id'];
  52. return $this->renderPartial('index', ['isWeixin' => $this->isWeixin,'customId' => $customId,'adminId' => $adminId,'merchantTag' => $merchantTag,'userTag' => $userTag,'hasHistoryPage' => $hasHistoryPage,'customId' => $customId, 'account'=>$account, 'themself' => $themself,'ourself' => $ourself,'emojiCode' => json_encode($emojiCode), 'clientId' => $clientId, 'swooleUrl'=>$swooleUrl]);
  53. }
  54. /**
  55. * 商家给客户发的模板消息,客户打开
  56. */
  57. public function actionTempletMsg(){
  58. $userId = Yii::$app->request->get('customId');
  59. $user = xhUserService::getById($userId);
  60. $merchant = $this->merchant;
  61. $merchantId = $this->merchantId;
  62. if(!empty($user)){
  63. $now = time();
  64. $userOpenId = $user['openId'];
  65. //获取最后一次会话时间。如果是首次会话或中断对话持续100秒,则通过发公众号通知有新消息
  66. $connect = Yii::$app->redis->executeCommand('GET', ["customer_{$userId}:merchant_{$merchantId}_new_msg_last_time"]);
  67. if(empty($connect) || ($now - $connect) > 100){
  68. $allTM = xhTMessageService::getList($merchantId);
  69. $shortTempId = 'OPENTM200605630';
  70. $tempId = $allTM[$shortTempId];
  71. $data = [
  72. "touser" => $userOpenId,"template_id" => $tempId,"url" => Yii::$app->params['frontUrl']."/chat/index?account=".$merchant['id'],
  73. "data" => ["first" => ["value" => "商家({$merchant['merchantName']}) 向您发来新消息","color" => "#173177"],
  74. "keyword1" => ["value" => '新消息',"color" => "#173177"],
  75. "keyword2" => ["value" => '文字',"color" => "#173177"],
  76. "remark" => ["value" => '点击查看详情',"color" => "#173177"]
  77. ]
  78. ];
  79. weixinUtil::sendTaskInform($data, $merchant);
  80. $now = (string)$now;
  81. Yii::$app->redis->executeCommand('SET', ["customer_{$userId}:merchant_{$merchantId}_new_msg_last_time", $now]);
  82. Yii::$app->redis->executeCommand('EXPIRE', ["customer_{$userId}:merchant_{$merchantId}_new_msg_last_time", 100]);//过期
  83. /*Yii::$app->redis->set("customer_{$userId}:merchant_{$merchantId}_new_msg_last_time", $now);
  84. Yii::$app->redis->expire("customer_{$userId}:merchant_{$merchantId}_new_msg_last_time", 100);*/
  85. }
  86. util::successInfo();
  87. //商家需要接收的消息。key里 第二个参数 ourself表示自己的消息 themself表示对方的消息 {~CHAT~} 表示分隔符
  88. //表情替换
  89. /*$imgUrl = $this->imgUrl;
  90. $emotionCode = qqFaceUtil::getEmojiCode($imgUrl);
  91. foreach($emotionCode as $key => $val){
  92. $zz = $val[0];
  93. $img = $val[1];
  94. $name = $val[2];
  95. $Content = str_replace($zz,$img,$Content);
  96. }
  97. Yii::$app->redis->executeCommand('LPUSH', ["merchant_get_{$merchantId}_{$userId}", $now.'{~CHAT~}themself{~CHAT~}'.$Content]);
  98. util::end();*/
  99. }
  100. }
  101. /**
  102. * swoole进程崩了,发送手机短信
  103. */
  104. public function actionSwooleProcessDead(){
  105. if(Yii::$app->request->isAjax){//判断ajax提交
  106. $swooleMsgKey = configDict::getCacheKey('swoolePhoneMsg');
  107. $sendTime = Yii::$app->redis->executeCommand('GET', [$swooleMsgKey]);
  108. if(empty($sendTime)){
  109. $error = Yii::$app->request->post('error');
  110. $error = empty($error) ? '未传递错误信息' : $error;
  111. Yii::$app->redis->executeCommand('SET', [$swooleMsgKey, time()]);
  112. Yii::$app->redis->executeCommand('EXPIRE', [$swooleMsgKey, 60*30]);//30分钟过期
  113. $mobile = '18259179840';
  114. $serverIp = util::serverIp();
  115. $msg = '服务器IP:' . $serverIp . '的Swoole进程中断,请登录服务器查看,报错:'.$error.'!';//避免用户过长时间无法正常使用相应功能
  116. $sms = new chuanglanSMS('N2890342','abc178c7');
  117. $re = $sms->send($mobile, $msg);
  118. }
  119. }
  120. }
  121. /**
  122. * 商家管理员发送消息
  123. */
  124. public function actionSendMsg()
  125. {
  126. $merchantId = $this->merchantId;
  127. $get = Yii::$app->request->get();
  128. if(isset($get['content']) == false || empty($get['content'])){
  129. echo Json::encode(['code' => 'A0002','msg' => 'content empty','data' => '']);
  130. Yii::$app->end();
  131. }
  132. if(isset($get['customId']) == false || empty($get['customId'])){
  133. echo Json::encode(['code' => 'A0002','msg' => 'customId empty','data' => '']);
  134. Yii::$app->end();
  135. }
  136. $customId = $get['customId'];
  137. $content = $get['content'];
  138. $now = time();
  139. $user = xhUserService::getById($customId);
  140. if(empty($user)){
  141. echo Json::encode(['code' => 'A0002','msg' => '消息发送失败,没有用户信息','data' => $data]);
  142. Yii::$app->end();
  143. }
  144. if($user['merchantId'] != $this->merchantId){
  145. echo Json::encode(['code' => 'A0002','msg' => '非法访问','data' => $data]);
  146. Yii::$app->end();
  147. }
  148. if(empty($user['subscribe'])){
  149. echo Json::encode(['code' => 'A0002','msg' => '消息发送失败,用户没有关注公众号','data' => $data]);
  150. Yii::$app->end();
  151. }
  152. $customOpenId = $user['openId'];
  153. $r = weixinUtil::sendMsg($content,$this->merchant,$customOpenId);
  154. if($r['errcode'] != 0){
  155. if($r['errcode'] == '45015'){
  156. echo Json::encode(['code' => 'A0002','msg' => '消息发送失败,超过48小时未跟对方联系,需对方主动与你联系','data' => $data]);
  157. Yii::$app->end();
  158. }
  159. echo Json::encode(['code' => 'A0002','msg' => '消息发送失败,原因未知','data' => $data]);
  160. Yii::$app->end();
  161. }
  162. //商家的历史消息
  163. Yii::$app->redis->executeCommand('ZADD', ["merchant_history_{$merchantId}_{$customId}", $now, $now.'{~CHAT~}ourself{~CHAT~}'.$content]);
  164. $createTime = date("Y-m-d H:i:s");
  165. $msg = $content;
  166. $data = ['msgBelongTo' => 'ourself','content' => $msg,'createTime' =>$createTime];
  167. echo Json::encode(['code' => 'A0001','msg' => 'success','data' => $data]);
  168. }
  169. /**
  170. * 商家管理员获取消息
  171. */
  172. public function actionGetMsg()
  173. {
  174. $get = Yii::$app->request->get();
  175. if(isset($get['customId']) == false || empty($get['customId'])){
  176. echo Json::encode(['code' => 'A0002','msg' => 'customId empty','data' => '']);
  177. Yii::$app->end();
  178. }
  179. $customId = $get['customId'];
  180. $merchantId = $this->merchantId;
  181. $conList = [];
  182. /*$content = Yii::$app->redis->executeCommand('RPOP', ["merchant_get_{$merchantId}_{$customId}"]);
  183. //已经获取此用户消息,将用户从新消息列表删除
  184. Yii::$app->redis->executeCommand('ZREM', ["merchant_{$merchantId}_new_msg", "{$customId}"]);
  185. if(!empty($content)){
  186. do{
  187. $arr = explode('{~CHAT~}',trim($content));
  188. if(count($arr) == 3){
  189. $time = $arr[0];
  190. $createTime = date("Y-m-d H:i:s",$time);
  191. $msgBelongTo = $arr[1];
  192. $msg = $arr[2];
  193. $conArr = ['msgBelongTo' => $msgBelongTo,'content' => $msg,'createTime' =>$createTime];
  194. $conList[] = $conArr;
  195. //商家的历史消息
  196. $r = Yii::$app->redis->executeCommand('ZADD', ["merchant_history_{$merchantId}_{$customId}",$time, $time.'{~CHAT~}'.$msgBelongTo.'{~CHAT~}'.$msg]);
  197. }
  198. $content = Yii::$app->redis->executeCommand('RPOP', ["merchant_get_{$merchantId}_{$customId}"]);
  199. }while(!empty($content));
  200. }*/
  201. while($content = Yii::$app->redis->executeCommand('RPOP', ["merchant_get_{$merchantId}_{$customId}"])){
  202. $arr = explode('{~CHAT~}',trim($content));
  203. if(count($arr) == 3){
  204. $time = $arr[0];
  205. $createTime = date("Y-m-d H:i:s",$time);
  206. $msgBelongTo = $arr[1];
  207. $msg = $arr[2];
  208. $conArr = ['msgBelongTo' => $msgBelongTo,'content' => $msg,'createTime' =>$createTime];
  209. $conList[] = $conArr;
  210. //保存客户发来的消息
  211. Yii::$app->redis->executeCommand('ZADD', ["merchant_history_{$merchantId}_{$customId}",$time, $time.'{~CHAT~}'.$msgBelongTo.'{~CHAT~}'.$msg]);
  212. }
  213. }
  214. echo Json::encode(['code' => 'A0001','msg' => 'success','data' => $conList]);
  215. }
  216. /**
  217. * 获取新消息用户 最近联系人
  218. */
  219. public function actionGetUser()
  220. {
  221. $merchantId = $this->merchantId;
  222. $userId = $this->adminId;
  223. //打开二维码的用户
  224. /*$callUserReturn = Yii::$app->redis->executeCommand('ZREVRANGE', ["merchant_{$merchantId}_new_call", 0, -1, 'WITHSCORES']);
  225. $callUserList = [];
  226. if(!empty($callUserReturn)){
  227. $i = 0;
  228. $x = 0;
  229. foreach($callUserReturn as $key => $val){
  230. $i++;
  231. if($i%2 == 0){
  232. $callUserList[$x]['time'] = $val;
  233. $x++;
  234. }else{
  235. $user = xhUserService::getById($val);
  236. $callUserList[$x]['userId'] = $val;
  237. $callUserList[$x]['userName'] = $user['userName'];
  238. }
  239. }
  240. //只显示最近一分查看二维码的用户
  241. $nowTime = time();
  242. foreach($callUserList as $cuKey => $cuVal){
  243. $cuTime = $cuVal['time'];
  244. $cuUserId = $cuVal['userId'];
  245. if(($nowTime - $cuTime) > 120 ){
  246. unset($callUserList[$cuKey]);
  247. Yii::$app->redis->executeCommand('ZREM', ["merchant_{$merchantId}_new_call", "{$cuUserId}"]);
  248. }
  249. }
  250. }*/
  251. //新消息
  252. $newUserReturn = Yii::$app->redis->executeCommand('ZREVRANGE', ["merchant_{$merchantId}_new_msg", 0,-1, 'WITHSCORES']);
  253. $newUserList = [];
  254. if(!empty($newUserReturn)){
  255. $i = 0;
  256. $x = 0;
  257. foreach($newUserReturn as $key => $val){
  258. $i++;
  259. if($i%2 == 0){
  260. $newUserList[$x]['time'] = $val;
  261. $x++;
  262. }else{
  263. $user = xhUserService::getById($val);
  264. $newUserList[$x]['userId'] = $val;
  265. $newUserList[$x]['userName'] = $user['userName'];
  266. }
  267. }
  268. }
  269. $historyUserList = [];
  270. //最近联系人保留最近30天的,更早的删除
  271. $now = time();
  272. $maxTime = $now - 2592000;
  273. $historyUserReturn = Yii::$app->redis->executeCommand('ZREMRANGEBYSCORE', ["merchant_{$merchantId}_history_chat_user", 0,$maxTime]);
  274. //最近联系
  275. $historyUserReturn = Yii::$app->redis->executeCommand('ZREVRANGE', ["merchant_{$merchantId}_history_chat_user", 0,-1, 'WITHSCORES']);
  276. if(!empty($historyUserReturn)){
  277. $i = 0;
  278. $x = 0;
  279. foreach($historyUserReturn as $key => $val){
  280. $i++;
  281. if($i%2 == 0){
  282. $historyUserList[$x]['time'] = $val;
  283. $x++;
  284. }else{
  285. $user = xhUserService::getById($val);
  286. $historyUserList[$x]['userId'] = $val;
  287. $historyUserList[$x]['userName'] = $user['userName'];
  288. }
  289. }
  290. }
  291. echo Json::encode(['code' => 'A0001','msg' => '','data' => ['new' => $newUserList,'history' =>$historyUserList]]);
  292. }
  293. /**
  294. * 获取我的消息历史
  295. */
  296. public function actionGetMyHistory()
  297. {
  298. $get = Yii::$app->request->get();
  299. if(isset($get['customId']) == false || empty($get['customId'])){
  300. echo Json::encode(['code' => 'A0002','msg' => 'customId empty','data' => '']);
  301. Yii::$app->end();
  302. }
  303. $customId = $get['customId'];
  304. $merchantId = $this->merchantId;
  305. $get = Yii::$app->request->get();
  306. //历史消息保留全部
  307. $now = time();
  308. $conList = [];
  309. $return = Yii::$app->redis->executeCommand('ZRANGE', ["merchant_history_{$merchantId}_{$customId}",0,-1,'WITHSCORES']);
  310. $conList = [];
  311. if(!empty($return)){
  312. $i = 0;
  313. $x = 0;
  314. $list = [];
  315. foreach($return as $key => $val){
  316. $i++;
  317. if($i%2 == 0){
  318. $list[$x]['time'] = $val;
  319. $x++;
  320. }else{
  321. $list[$x]['content'] = $val;
  322. }
  323. }
  324. foreach($list as $key => $val){
  325. $time = $val['time'];
  326. $content = $val['content'];
  327. $arr = explode('{~CHAT~}',trim($content));
  328. if(count($arr) == 3){
  329. $createTime = date("Y-m-d H:i:s",$arr[0]);
  330. $msgBelongTo = $arr[1];
  331. $msg = $arr[2];
  332. $conArr = ['msgBelongTo' => $msgBelongTo,'content' => $msg,'createTime' =>$createTime];
  333. $conList[] = $conArr;
  334. }
  335. }
  336. }
  337. echo Json::encode(['code' => 'A0001','msg' => 'success','data' => $conList]);
  338. }
  339. public function actionUploadFile() {
  340. $fileInfo = array('url' => '');
  341. if (empty ( $_FILES )) {
  342. $fileInfo = ['code' => 'A0002','msg' => '没有图片资源上传'];
  343. echo Json::encode($fileInfo);
  344. Yii::$app->end();
  345. }
  346. $post = Yii::$app->request->post();
  347. $userId = isset($post['userId']) ? $post['userId'] : 0;
  348. $customId = isset($post['customId']) ? $post['customId'] : 0;
  349. $customInfo = xhUserService::getById($customId);
  350. $merchantId = $this->merchantId;
  351. $now = time();
  352. if(isset($customInfo['merchantId']) == false || $customInfo['merchantId'] != $merchantId){
  353. $fileInfo = ['code' => 'A0002','msg' => '聊天对象非法'];
  354. echo Json::encode($fileInfo);
  355. Yii::$app->end();
  356. }
  357. $preFileName = substr(md5($userId),-4);
  358. $fileName = $preFileName.stringUtil::buildOrderNo();
  359. $tempFile = $_FILES['Filedata']['tmp_name'];
  360. $fileParts = explode('.',$_FILES["Filedata"]['name']);
  361. $extName = $fileParts [1];
  362. $webRoot = Yii::getAlias('@webroot').'/../../images';
  363. $folder = '/weixinChat/'. date ('Y').'/'.date('m').'/'.date("d").'/';
  364. if (! file_exists ($webRoot.$folder))
  365. mkdir ($webRoot.$folder, 0777,true);
  366. $saveFile = $webRoot.$folder.$fileName.'.'.$extName;
  367. move_uploaded_file($tempFile, $saveFile);
  368. //生成300缩略图
  369. $dstImg300 = $webRoot.$folder.$fileName.'_300.'.$extName;
  370. $img2thumbReturn = util::img2thumb($saveFile, $dstImg300, $width = 300, $height = 0, $cut = 0, $proportion = 0);
  371. $srcImgWidth = isset($img2thumbReturn['width']) ? $img2thumbReturn['width'] : 0;
  372. $srcImgHeight = isset($img2thumbReturn['height']) ? $img2thumbReturn['height'] : 0;
  373. //上传图片到微信
  374. $return = weixinUtil::uploadTempMedia($this->merchant,$saveFile);
  375. if(isset($return['errcode'])){
  376. $errCode = $return['errcode'];
  377. $errList = weixinUtil::$errCode;
  378. $msg = isset($errList[$errCode]) ? $errList[$errCode] : '{上传图片失败}';
  379. echo Json::encode(['code' => 'A0002','msg' => $msg,'data' => $data]);
  380. Yii::$app->end();
  381. }
  382. if(isset($return['media_id'])){
  383. $mediaId = $return['media_id'];
  384. $customOpenId = $customInfo['openId'];
  385. $r = weixinUtil::sendImgMsg($mediaId, $this->merchant,$customOpenId);
  386. if($r['errcode'] != 0){
  387. $errCode = $r['errcode'];
  388. $errList = weixinUtil::$errCode;
  389. $msg = isset($errList[$errCode]) ? $errList[$errCode] : '{发送消息失败}';
  390. echo Json::encode(['code' => 'A0002','msg' => $msg,'data' => $data]);
  391. Yii::$app->end();
  392. }
  393. $invalid = '/images/invalid.jpg';
  394. $content = '<img widthNum="'.$srcImgWidth.'" heightNum="'.$srcImgHeight.'" style="width:100px;height:auto;" onclick="previewImg(this)" onerror="javascript:this.src=\''.$invalid.'\'" src="'.$this->imgUrl.$folder.$fileName.'_300.'.$extName.'" bigImgSrc="'.$this->imgUrl.$folder.$fileName.'.'.$extName.'" />';
  395. //商家的历史消息
  396. Yii::$app->redis->executeCommand('ZADD', ["merchant_history_{$merchantId}_{$customId}", $now, $now.'{~CHAT~}ourself{~CHAT~}'.$content]);
  397. $createTime = date("Y-m-d H:i:s");
  398. $data = [
  399. 'msgBelongTo' => 'ourself',
  400. 'content' => $content,
  401. 'createTime' =>$createTime,
  402. 'imgUrl' => $folder.$fileName.'.'.$extName,
  403. 'imgPreUrl' =>$folder.$fileName,
  404. 'extName' =>'.'.$extName,
  405. ];
  406. echo Json::encode(['code' => 'A0001','msg' => 'success','data' => $data]);
  407. Yii::$app->end();
  408. }
  409. echo Json::encode(['code' => 'A0002','msg' => '操作没有成功……','data' => []]);
  410. }
  411. public function actionGetChatVoice()
  412. {
  413. $get = Yii::$app->request->get();
  414. $voiceId = isset($get['voiceId']) ? $get['voiceId'] : '';
  415. $return = weixinUtil::getTempMaterial($this->merchant, $voiceId);
  416. echo Json::encode($return);
  417. }
  418. }