ChatController.php 17 KB

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