MiniEventPush.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. <?php
  2. namespace app\api\controller;
  3. use app\model\Employee;
  4. use app\model\Miniprogram;
  5. use app\model\ShareLog;
  6. use app\model\ShortUrl;
  7. use app\model\User;
  8. use DOMDocument;
  9. use openssl\Aes;
  10. use wx\offiaccount\Event;
  11. use think\facade\Cache;
  12. use wx\miniprogram\msg\CustomerServiceMessage;
  13. use app\model\AgentUser;
  14. use app\model\MaterialEvidence;
  15. use app\model\CompanyStrength as CompanyStrengthModel;
  16. use app\model\Wechat;
  17. use app\model\AgentArticle;
  18. use app\model\Company;
  19. /**
  20. * 微信事件接受类
  21. *
  22. */
  23. class MiniEventPush
  24. {
  25. private $client = [
  26. 'zyzs' => 1,
  27. 'lite' => 2,
  28. 'mini' => 3
  29. ];
  30. private $mini;
  31. public function catch($action)
  32. {
  33. $id = $this->client[$action];
  34. $this->mini = Wechat::where([['id', '=', $id]])->find();
  35. $signature = $_GET['signature'];
  36. $timestamp = $_GET['timestamp'];
  37. $nonce = $_GET['nonce'];
  38. $token = $this->mini->token;
  39. $encodingAesKey = $this->mini->encodingAesKey;
  40. $appId = $this->mini->appid;
  41. $eventObj = new Event($token, $encodingAesKey, $appId);
  42. // token验证
  43. if (isset($_GET["echostr"])) {
  44. if ($eventObj->checkToken($token, $signature, $timestamp, $nonce)) {
  45. return $_GET["echostr"];
  46. }
  47. }
  48. $this->index($eventObj);
  49. }
  50. private function index(Event &$eventObj)
  51. {
  52. $timestamp = $_GET['timestamp'];
  53. $nonce = $_GET['nonce'];
  54. // // 消息体加密
  55. $msgSignature = $_GET['msg_signature'];
  56. // 推送消息内容获取
  57. $postXml = file_get_contents('php://input');
  58. trace('消息内容:' . $postXml, "debug");
  59. $rs = $eventObj->decryptMsg($msgSignature, $timestamp, $nonce, $postXml, $msg);
  60. trace('消息内容:' . $msg, "debug");
  61. if ($rs !== 0) {
  62. trace('消息内容解析失败:' . $postXml, "error");
  63. return 'success';
  64. }
  65. $xml = new DOMDocument();
  66. $xml->loadXML($msg);
  67. $msgType = $xml->getElementsByTagName('MsgType')->item(0)->nodeValue;
  68. $rs = $this->$msgType($xml);
  69. if ($rs === null) {
  70. trace($msg, 'log');
  71. return 'success';
  72. }
  73. $rs = json_decode($rs, true);
  74. if ($rs['errcode'] == 0) {
  75. trace($rs, 'log');
  76. return 'success';
  77. }
  78. trace($rs, 'error');
  79. }
  80. /**
  81. * 文本消息
  82. */
  83. public function text($xml)
  84. {
  85. $fromUserName = $xml->getElementsByTagName('FromUserName')->item(0)->nodeValue;
  86. $content = $xml->getElementsByTagName('Content')->item(0)->nodeValue;
  87. $rs = null;
  88. switch ($content) {
  89. case '关注公众号': // 关注公众号
  90. $rs = $this->officialImg($fromUserName);
  91. break;
  92. case '分享链接': // 分享链接
  93. $rs = $this->shareLink($fromUserName);
  94. break;
  95. }
  96. return $rs;
  97. }
  98. /**
  99. * 图片消息
  100. */
  101. public function image($xml)
  102. {
  103. }
  104. /**
  105. * 小程序卡消息回复
  106. */
  107. public function miniprogrampage($xml)
  108. {
  109. $fromUserName = $xml->getElementsByTagName('FromUserName')->item(0)->nodeValue;
  110. $title = $xml->getElementsByTagName('Title')->item(0)->nodeValue;
  111. $rs = null;
  112. switch ($title) {
  113. case '关注公众号': // 关注公众号
  114. $rs = $this->officialImg($fromUserName);
  115. break;
  116. case '获取分享链接': // 分享链接
  117. $rs = $this->shareLink($fromUserName, 'h5');
  118. break;
  119. }
  120. return $rs;
  121. }
  122. /**
  123. * 事件消息(用户进入客服事件)
  124. * @param DOMDocument $xml 消息内容,DOMDocument对象
  125. * @return string|null 返回消息发送结果,未发送返回null
  126. */
  127. public function event($xml)
  128. {
  129. // 获取事件发生人
  130. $fromUserName = $xml->getElementsByTagName('FromUserName')->item(0)->nodeValue;
  131. // 事件类型
  132. $event = $xml->getElementsByTagName('Event')->item(0)->nodeValue;
  133. // 消息最后一次发送日期 (null, 今天以前,今天)
  134. $lastSendData = cache("${fromUserName}_${event}");
  135. $now = date('Y-m-d');
  136. // 如果今天发送过, 则不发送
  137. if ($now == $lastSendData) return null;
  138. // 更新最后发送时间
  139. cache("${fromUserName}_${event}", $now);
  140. // 消息内容
  141. $msg = "欢迎使用营销数字化系统!!!";
  142. $data = [
  143. 'touser' => $fromUserName,
  144. 'msgtype' => 'text',
  145. 'text' => ['content' => $msg]
  146. ];
  147. // 调用消息发送类,返回消息发送结果
  148. return (new CustomerServiceMessage)->send($this->mini->accesstoken, $data);
  149. }
  150. /**
  151. * 发送公众号图片
  152. */
  153. private function officialImg($openid)
  154. {
  155. $serviceMsgObj = new CustomerServiceMessage();
  156. // 素材上传
  157. if (!$mediaId = Cache::get('zqxg_official_img_media_id')) {
  158. // 上传文件
  159. $data = [
  160. 'media' => curl_file_create(realpath($_SERVER['DOCUMENT_ROOT'] . '/static/img/official.jpg'))
  161. ];
  162. $rs = $serviceMsgObj->uploadTempMedia($this->mini->accesstoken, $data);
  163. trace($rs, 'log');
  164. $rs = json_decode($rs, true);
  165. if (isset($rs['errcode']) && isset($rs['errmsg']) && $rs['errcode'] != 0 && $rs['errmsg'] != 'ok') {
  166. trace($rs, 'error');
  167. return;
  168. }
  169. $mediaId = $rs['media_id'];
  170. Cache::set('zqxg_official_img_media_id', $rs['media_id'], 86400 * 3);
  171. }
  172. $data = [
  173. 'touser' => $openid,
  174. 'msgtype' => 'image',
  175. 'image' => ['media_id' => $mediaId]
  176. ];
  177. return $serviceMsgObj->send($this->mini->accesstoken, $data);
  178. }
  179. /**
  180. * 发送分享链接
  181. */
  182. private function shareLink($openid, $type = "mini")
  183. {
  184. // 获取用户id
  185. $userIds = User::where('mini_openid', $openid)->column('id');
  186. // 获取员工
  187. $employeeIds = Employee::where([['uid', 'in', $userIds], ['state', '=', '在职']])->column('id');
  188. // 获取经纪人
  189. $agentIds = AgentUser::where([['uid', 'in', $userIds]])->column('id');
  190. if (empty($employeeIds) && empty($agentIds)) {
  191. trace('员工或者经纪人不存在,无法获取链接', 'error');
  192. return;
  193. }
  194. // 查找用户要分享的内容编号
  195. if (!empty($employeeIds) && empty($agentIds)) {
  196. $share = ShareLog::where([['employee_id', 'in', $employeeIds], ['agent_id', '=', null]])->order('share_time desc')->find();
  197. //$share = ShareLog::where([['employee_id', 'in', $employeeIds]])->order('share_time desc')->find();
  198. } elseif(!empty($employeeIds) && !empty($agentIds)) {
  199. $where1 = [
  200. ['employee_id', 'in', $employeeIds],
  201. ['agent_id', '=', null]
  202. ];
  203. $where2 = [
  204. ['agent_id', 'in', $agentIds]
  205. ];
  206. $share = ShareLog::whereOr([$where1, $where2])->order('share_time desc')->find();
  207. } else {
  208. $share = ShareLog::where([['agent_id', 'in', $agentIds]])->order('share_time desc')->find();
  209. }
  210. if (empty($share)) {
  211. trace('分享内容获取失败', 'error');
  212. return;
  213. }
  214. $strpos = strpos($share->type, '_vr');
  215. if ($strpos) {
  216. $class = 'app\\model\\' . str_replace('_vr', '', $share->type);
  217. $share_type = str_replace('_vr', '', $share->type);
  218. if ($share_type == 'Company') $class = 'app\\model\\CompanyVrshow';
  219. if ($share_type == 'Housetype') {
  220. $class = 'app\\model\\BuildingHousetype';
  221. $share->type = $share_type = 'BuildingHousetype';
  222. }
  223. } elseif($share->type == 'housetype' || $share->type == 'Housetype'){
  224. $class = 'app\\model\\BuildingHousetype' ;
  225. $share->type = $share_type = 'BuildingHousetype';
  226. } else {
  227. $class = 'app\\model\\' . $share->type;
  228. $share_type = $share->type;
  229. }
  230. if (!class_exists($class)) {
  231. trace('分享类不存在', 'error');
  232. return;
  233. }
  234. $obj = new $class();
  235. $data = $obj->find($share->data_id);
  236. if ($share_type == 'Construction' || $share_type == 'Building') {
  237. $data['title'] = $data['name'];
  238. }
  239. if ($share_type == 'BuildingHousetype'){
  240. $data['title'] = $data['room'] . '室' . $data['hall'] . '厅' . $data['bathroom'] . '卫';
  241. }
  242. if($share_type == 'Company'){
  243. $company = Company::where([['root_id','=',$data['root_id']]])->field('company_name,logo')->find();
  244. $data['title'] = $data['name'].'展厅全景VR';
  245. $data['logo'] = $company['logo'];
  246. }
  247. if($share_type == 'BuildingProgress') $data['title'] = $data['name'];
  248. $imgUrlParam = [
  249. 'Video' => [
  250. 'param' => 'video_url',
  251. 'ext' => '?x-oss-process=video/snapshot,t_100,f_jpg,w_640,h_320,m_fast'
  252. ],
  253. 'MaterialCase' => [
  254. 'param' => 'cover_img',
  255. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  256. ],
  257. 'MaterialEvidence' => [
  258. 'param' => 'pics',
  259. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  260. ],
  261. 'Activity' => [
  262. 'param' => 'cover',
  263. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  264. ],
  265. 'Article' => [
  266. 'param' => 'cover_img',
  267. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  268. ],
  269. 'Construction' => [
  270. 'param' => 'cover',
  271. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  272. ],
  273. 'Building' => [
  274. 'param' => 'cover',
  275. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  276. ],
  277. 'CompanyStrength' => [
  278. 'param' => 'pics',
  279. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  280. ],
  281. 'AgentArticle' => [
  282. 'param' => 'file',
  283. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  284. ],
  285. 'BuildingHousetype' =>[
  286. 'param' => 'house_img',
  287. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  288. ],
  289. 'Company' => [
  290. 'param' => 'logo',
  291. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  292. ],
  293. 'BuildingProgress' => [
  294. 'param' => 'img',
  295. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  296. ]
  297. ];
  298. //口碑中有图片和视频区分
  299. if ($share_type == 'MaterialEvidence') {
  300. $info = MaterialEvidence::where('id', $share->data_id)->find();
  301. if ($info->difference == 1) {
  302. if ($info->cover) { //如果封面图存在
  303. $imgUrlParam['MaterialEvidence'] = [
  304. 'param' => 'cover',
  305. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  306. ];
  307. } else { //封面图不存在
  308. $imgUrlParam['MaterialEvidence'] = [
  309. 'param' => 'pics',
  310. 'ext' => '?x-oss-process=video/snapshot,t_100,f_jpg,w_640,h_320,m_fast'
  311. ];
  312. }
  313. }
  314. } elseif ($share_type == 'CompanyStrength') {
  315. $info = CompanyStrengthModel::where('id', $share->data_id)->find();
  316. if ($info->difference == 1) {
  317. if ($info->cover) {
  318. $imgUrlParam['CompanyStrength'] = [
  319. 'param' => 'cover',
  320. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  321. ];
  322. } else {
  323. $imgUrlParam['CompanyStrength'] = [
  324. 'param' => 'pics',
  325. 'ext' => '?x-oss-process=video/snapshot,t_100,f_jpg,w_640,h_320,m_fast'
  326. ];
  327. }
  328. }
  329. } elseif ($share_type == 'AgentArticle') {
  330. //经纪人文章会出现多图或者多视频,type:1视频type2图片
  331. $info = AgentArticle::where('id', $share->data_id)->find();
  332. if ($info->file) {
  333. if ($info->type == 2) {
  334. $imgUrlParam['AgentArticle'] = [
  335. 'param' => 'file',
  336. 'ext' => '?x-oss-process=image/resize,m_fill,h_320,w_640'
  337. ];
  338. } else {
  339. $imgUrlParam['AgentArticle'] = [
  340. 'param' => 'file',
  341. 'ext' => '?x-oss-process=video/snapshot,t_100,f_jpg,w_640,h_320,m_fast'
  342. ];
  343. }
  344. }
  345. }
  346. if (is_array($data[$imgUrlParam[$share_type]['param']])) {
  347. $thumb = $data[$imgUrlParam[$share_type]['param']][0] . $imgUrlParam[$share_type]['ext'];
  348. } else {
  349. $thumb = $data[$imgUrlParam[$share_type]['param']] . $imgUrlParam[$share_type]['ext'];
  350. }
  351. $emp = Employee::where('id', $share->employee_id)->find();
  352. trace('312行', 'debug');
  353. $clientType = Miniprogram::where('root_id', $emp->root_id)->value('notify');
  354. trace('314行', 'debug');
  355. // $v = [
  356. // 'type' => $share->type,
  357. // 'id' => $data->id,
  358. // 'employee_id' => $share->employee_id
  359. // ];
  360. // $root_id = Employee::where('id', $share->employee_id)->value('root_id');
  361. // $v = http_build_query($v);
  362. // $aec = new Aes(config('app.jwt_key'));
  363. // $key = $aec->encrypt($v);
  364. // url生成
  365. if ($type == 'mini') {
  366. trace('326行,mini', 'debug');
  367. $param = $this->getParam(lcfirst($share->type), $emp->uid, $share->data_id, $clientType, $share->other_data);
  368. trace($param, 'debug');
  369. trace('328行', 'debug');
  370. // 存储参数
  371. $urldata = [
  372. 'content' => json_encode($param),
  373. 'type' => 'share'
  374. ];
  375. trace($urldata);
  376. $shortUrl = new ShortUrl();
  377. trace($shortUrl);
  378. $shortUrl->save($urldata);
  379. // 将ID进行
  380. $query = dec52($shortUrl->id);
  381. $shortUrl->uri = $query;
  382. $shortUrl->save();
  383. } else {
  384. trace('341行,mini else', 'debug');
  385. if ($strpos){ // vr
  386. $param = [
  387. 'id' => $share->data_id,
  388. 'type' => $share->type,
  389. 'employee_id' => $share->employee_id,
  390. 'agent_id' => $share->agent_id
  391. ];
  392. $param = http_build_query($param);
  393. $aes = new Aes(config('app.jwt_key'));
  394. $query_arr = [
  395. 'r'=> $emp->root_id,
  396. 'p'=> $aes->encrypt($param),
  397. 'id'=> $share->data_id,
  398. 'type'=> lcfirst($share_type),
  399. 'vr_link'=> $share->other_data
  400. ];
  401. $query = 'index/index/vr.html?' . http_build_query($query_arr);
  402. } elseif($share_type == 'BuildingHousetype'){
  403. $param = [
  404. 'id' => $share->data_id,
  405. 'type' => $share->type,
  406. 'employee_id' => $share->employee_id,
  407. 'agent_id' => $share->agent_id
  408. ];
  409. $param = http_build_query($param);
  410. $aes = new Aes(config('app.jwt_key'));
  411. $query = 'index/index/housetype/id/'.$share->data_id.'/r/' . $emp->root_id . '/p/' . $aes->encrypt($param);
  412. } elseif($share_type == 'BuildingProgress') {
  413. $param = [
  414. 'id' => $share->data_id,
  415. 'type' => $share->type,
  416. 'employee_id' => $share->employee_id,
  417. 'agent_id' => $share->agent_id
  418. ];
  419. $param = http_build_query($param);
  420. $aes = new Aes(config('app.jwt_key'));
  421. $query = 'index/index/progress_detail/id/'.$share->data_id.'/r/' . $emp->root_id . '/p/' . $aes->encrypt($param);
  422. } else {
  423. $param = [
  424. 'id' => $share->data_id,
  425. 'type' => $share->type,
  426. 'employee_id' => $share->employee_id,
  427. 'agent_id' => $share->agent_id
  428. ];
  429. $param = http_build_query($param);
  430. $aes = new Aes(config('app.jwt_key'));
  431. $query = 'index/index/content/r/' . $emp->root_id . '/p/' . $aes->encrypt($param);
  432. }
  433. }
  434. $domain = request()->domain();
  435. $url = $domain . '/' . $query;
  436. $data = [
  437. 'touser' => $openid,
  438. 'msgtype' => 'link',
  439. 'link' => [
  440. 'title' => $data['title'],
  441. 'description' => $data['title'],
  442. 'url' => $url,
  443. 'thumb_url' => $thumb
  444. ]
  445. ];
  446. trace('分享链接内容:' . json_encode($data), 'debug');
  447. return (new CustomerServiceMessage)->send($this->mini->accesstoken, $data);
  448. }
  449. public function getParam($type, $shareUid, $id, $clientType, $other_data)
  450. {
  451. if (strpos($type, '_vr')) {
  452. $data = [
  453. 'client_type' => $clientType,
  454. 'id' => $id,
  455. 'path' => "/share/pages/other/other",
  456. 'query' => "aid=" . $id . "&uid=" . $shareUid . "&ctp=" . $clientType,
  457. 'type' => $type,
  458. 'user_id' => $shareUid,
  459. 'other_data'=> $other_data
  460. ];
  461. return $data;
  462. }
  463. $params = [
  464. 'article' => [
  465. 'client_type' => $clientType,
  466. 'id' => $id,
  467. 'path' => "/share/pages/articleshare/articleshare",
  468. 'query' => "aid=" . $id . "&userid=" . $shareUid,
  469. 'type' => "article",
  470. 'user_id' => $shareUid,
  471. 'other_data'=> $other_data
  472. ],
  473. 'video' => [
  474. 'client_type' => $clientType,
  475. 'id' => $id,
  476. 'path' => "/share/pages/videoshare/videoshare",
  477. 'query' => "vid=" . $id . "&userid=" . $shareUid,
  478. 'type' => "video",
  479. 'user_id' => $shareUid,
  480. 'other_data'=> $other_data
  481. ],
  482. 'materialEvidence' => [
  483. 'client_type' => $clientType,
  484. 'id' => $id,
  485. 'path' => "/share/pages/publicpraisemsg/publicpraisemsg",
  486. 'query' => "eid=" . $id . "&uid=" . $shareUid,
  487. 'type' => "materialEvidence",
  488. 'user_id' => $shareUid,
  489. 'other_data'=> $other_data
  490. ],
  491. 'materialCase' => [
  492. 'client_type' => $clientType,
  493. 'id' => $id,
  494. 'path' => "/share/pages/materialcase/materialcase",
  495. 'query' => "cid=" . $id . "&uid=" . $shareUid,
  496. 'type' => "materialCase",
  497. 'user_id' => $shareUid,
  498. 'other_data'=> $other_data
  499. ],
  500. 'activity' => [
  501. 'client_type' => $clientType,
  502. 'id' => $id,
  503. 'path' => "/share/pages/activitymsg/activitymsg",
  504. 'query' => "aid=" . $id . "&uid=" . $shareUid,
  505. 'type' => "activity",
  506. 'user_id' => $shareUid,
  507. 'other_data'=> $other_data
  508. ],
  509. 'construction' => [
  510. 'client_type' => $clientType,
  511. 'id' => $id,
  512. 'path' => "/share/pages/constructsite/constructsite",
  513. 'query' => "aid=" . $id . "&uid=" . $shareUid,
  514. 'type' => "construction",
  515. 'user_id' => $shareUid,
  516. 'other_data'=> $other_data
  517. ],
  518. 'building' => [
  519. 'client_type' => $clientType,
  520. 'id' => $id,
  521. 'path' => "/share/pages/buildprogress/buildprogress",
  522. 'query' => "aid=" . $id . "&uid=" . $shareUid,
  523. 'type' => "building",
  524. 'user_id' => $shareUid,
  525. 'other_data'=> $other_data
  526. ],
  527. 'companyStrength' => [
  528. 'client_type' => $clientType,
  529. 'id' => $id,
  530. 'path' => "/share/pages/companystrengthmsg/companystrengthmsg",
  531. 'query' => "eid=" . $id . "&uid=" . $shareUid . "&ctp=" . $clientType,
  532. 'type' => "CompanyStrength",
  533. 'user_id' => $shareUid,
  534. 'other_data'=> $other_data
  535. ],
  536. 'buildingHousetype' => [
  537. 'client_type' => $clientType,
  538. 'id' => $id,
  539. 'path' => "/index/pages/housetype/housetype",
  540. 'query' => "d=" . $id . "&u=" . $shareUid . "&c=" . $clientType,
  541. 'type' => "BuildingHousetype",
  542. 'user_id' => $shareUid,
  543. 'other_data'=> $other_data
  544. ],
  545. 'buildingProgress' => [
  546. 'client_type' => $clientType,
  547. 'id' => $id,
  548. 'path' => "/share/pages/buildShare/buildShare",
  549. 'query' => "uid=" . $shareUid . "&cid=" . $id . "&ctp=" . $clientType,
  550. 'type' => "BuildingProgress",
  551. 'user_id' => $shareUid,
  552. 'other_data'=> $other_data
  553. ]
  554. ];
  555. return $params[$type];
  556. }
  557. }