WeixinController.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <?php
  2. namespace app\controllers;
  3. use app\common\components\SiteUrl;
  4. use app\components\WeiXin\WeiXin;
  5. use app\modules\admin\models\Config;
  6. use app\modules\plugins\models\MpReply;
  7. use app\modules\ucenter\models\UserOpenAuth;
  8. use app\modules\doc\models\DocReal;
  9. use app\modules\doc\models\DocDowncode;
  10. use Yii;
  11. use yii\web\Controller;
  12. class WeixinController extends Controller
  13. {
  14. public $enableCsrfValidation = false;
  15. public $mpconfig;
  16. public $imageconfig;
  17. public function init()
  18. {
  19. parent::init();
  20. //系统配置的公众号
  21. $mpconfigResult = Config::find()->where("name='mp'")->one();
  22. $this->mpconfig = string2array($mpconfigResult->value);
  23. //图片配置
  24. $imageconfigResult = Config::find()->where("name='imageconfig'")->one();
  25. $this->imageconfig = string2array($imageconfigResult->value);
  26. }
  27. public function traceHttp()
  28. {
  29. $this->logger("QUERY_STRING:".$_SERVER["QUERY_STRING"]);
  30. }
  31. public function logger($log_content)
  32. {
  33. $max_size = 500000;
  34. $log_filename = Yii::$app->params['wxPath'].'log.xml';
  35. if(file_exists($log_filename) and (abs(filesize($log_filename)) > $max_size)){unlink($log_filename);}
  36. file_put_contents($log_filename, date('Y-m-d H:i:s').$log_content."\r\n", FILE_APPEND);
  37. }
  38. //扫码关注登录/注册(因为获取不到昵称头像,暂时弃用)
  39. public function actionOpenauth()
  40. {
  41. $args = array(
  42. 'appId'=>Yii::$app->params['openauth']['mpAppId'],
  43. 'appSecret'=>Yii::$app->params['openauth']['mpAppSecret'],
  44. 'token'=>Yii::$app->params['openauth']['mpToken'],
  45. );
  46. $weixin = new WeiXin($args);
  47. $weixin->init();
  48. $this->traceHttp();
  49. $result = $weixin->receiveMsg();
  50. $this->logger(var_export($result,true));
  51. if(empty($result))
  52. {
  53. ob_clean();
  54. $weixin->valid();
  55. }
  56. else
  57. {
  58. $result['eventKey'] = str_replace('qrscene_','',$result['eventKey']);
  59. //扫码事件处理
  60. if(!empty($result['eventKey'])&&($result['event']=='SCAN'||$result['event']=='subscribe'))
  61. {
  62. $scene_id = $result['eventKey'];
  63. //扫码关注登录(PC)
  64. if(strpos($scene_id,'mplogin_')!==false)
  65. {
  66. $this->initOpenAuth($weixin,$result,$scene_id);
  67. }
  68. }
  69. }
  70. }
  71. //公众号用到的接口
  72. public function actionMp()
  73. {
  74. $args = array(
  75. 'appId'=>Yii::$app->params['mp']['mpAppId'],
  76. 'appSecret'=>Yii::$app->params['mp']['mpAppSecret'],
  77. 'token'=>Yii::$app->params['mp']['mpToken'],
  78. );
  79. $weixin = new WeiXin($args);
  80. $weixin->init();
  81. $this->traceHttp();
  82. $result = $weixin->receiveMsg();
  83. //$this->logger(var_export($result,true));
  84. if(empty($result))
  85. {
  86. ob_clean();
  87. $weixin->valid();
  88. }
  89. else
  90. {
  91. //被动回复
  92. if($result['msgType']!='event'&&!empty($result['content'])&&Yii::$app->params['mp']['autoreply'])
  93. {
  94. $reply = MpReply::find()->where("keywords like '%".$result['content']."%'")->orderBy(['list_order'=>SORT_ASC])->one();
  95. if(empty($reply))//默认没有匹配发送文档搜索入口
  96. {
  97. $picurl = $this->_getDowntipImg();
  98. //回复搜索页
  99. $url = SiteUrl::searchForWeixin(getSysconfigValue('default_search_model'),$result['content']);
  100. $articles = array(
  101. array(
  102. 'title'=>'"'.$result['content'].'"'.'的搜索结果',
  103. 'description'=>'点击查看所有内容',
  104. 'picurl'=>$picurl,
  105. 'url'=>urldecode($url)
  106. ),
  107. );
  108. $weixin->responseMsg(array('type'=>'news','count'=>'1'),$articles);
  109. }
  110. else
  111. {
  112. $this->sendReply($weixin,$reply);
  113. }
  114. exit;
  115. }
  116. $result['eventKey'] = str_replace('qrscene_','',$result['eventKey']);
  117. //扫码事件处理
  118. if(!empty($result['eventKey'])&&($result['event']=='SCAN'||$result['event']=='subscribe'))
  119. {
  120. $scene_id = $result['eventKey'];
  121. //$this->logger($scene_id);
  122. //扫码关注登录(PC)
  123. if(strpos($scene_id,'mplogin_')!==false)
  124. {
  125. //统一到登录注册配置的公众号信息
  126. $args = array(
  127. 'appId'=>Yii::$app->params['openauth']['mpAppId'],
  128. 'appSecret'=>Yii::$app->params['openauth']['mpAppSecret'],
  129. 'token'=>Yii::$app->params['openauth']['mpToken'],
  130. );
  131. $weixin = new WeiXin($args);
  132. $weixin->init();
  133. $this->traceHttp();
  134. $result = $weixin->receiveMsg();
  135. $openAuth = $this->initOpenAuth($weixin,$result,$scene_id);
  136. if(!empty($openAuth))
  137. {
  138. //判断用户资料是否写入
  139. $checkUserInfo = string2array(base64_decode($openAuth->user_info));
  140. if(!$checkUserInfo['nick_name'])
  141. {
  142. $picurl = $this->_getDowntipImg('mplogin');
  143. $newsurl = SiteUrl::mpLogin($scene_id,1);
  144. $articles = array(
  145. array(
  146. 'title'=>'',
  147. 'description'=>'',
  148. 'picurl'=>$picurl,
  149. 'url'=>urldecode($newsurl)
  150. ),
  151. array(
  152. 'title'=>'点击授权登录>>',
  153. 'description'=>'',
  154. 'picurl'=>'',
  155. 'url'=>urldecode($newsurl)
  156. ),
  157. );
  158. $weixin->responseMsg(array('type'=>'news','count'=>'2'),$articles);
  159. exit;
  160. }
  161. else
  162. {
  163. $picurl = $this->_getDowntipImg('mpsuccess');
  164. $newsurl = WAP_URL;
  165. $articles = array(
  166. array(
  167. 'title'=>'',
  168. 'description'=>'',
  169. 'picurl'=>$picurl,
  170. 'url'=>urldecode($newsurl)
  171. ),
  172. array(
  173. 'title'=>'您已在电脑端登录成功!',
  174. 'description'=>'',
  175. 'picurl'=>'',
  176. 'url'=>urldecode($newsurl)
  177. ),
  178. );
  179. //附加其他推送
  180. $weixin->responseMsg(array('type'=>'news','count'=>'2'),$articles);
  181. exit;
  182. }
  183. }
  184. }
  185. //扫码订阅消息通知(PC)
  186. if(strpos($scene_id,'mpmsg_')!==false&&Yii::$app->params['mp']['openmsg'])
  187. {
  188. $this->initMpMsg($weixin,$result,$scene_id);
  189. }
  190. //首次关注推送消息
  191. if(Yii::$app->params['mp']['autoreply']&&$result['event']=='subscribe')
  192. {
  193. //自动消息(客服消息)
  194. $customerMsg = \app\modules\plugins\models\MpReply::find()->where("focus=1")->orderBy(['list_order'=>SORT_ASC])->one();
  195. if(!empty($customerMsg))
  196. {
  197. $data = string2array($customerMsg->data);
  198. $return = $weixin->sendCustomermsg($customerMsg->msg_type,$result['fromUsername'],$data);
  199. //$this->logger(var_export($return,true));
  200. }
  201. //再增加一条关注回复
  202. if(strpos($scene_id,'freedowndoc_')===false){
  203. $reply = \app\modules\plugins\models\MpReply::find()->where("focus=1 and id!='".$customerMsg->id."'")->orderBy(['list_order'=>'SORT_ASC'])->one();
  204. if(!empty($reply))
  205. {
  206. $this->sendReply($weixin,$reply);
  207. }
  208. }
  209. }
  210. //免费扫码下载
  211. if(strpos($scene_id,'freedowndoc_')!==false)
  212. {
  213. $docId = str_replace("freedowndoc_","",$scene_id);
  214. $doc = DocReal::findOne($docId);
  215. $downCode = DocDowncode::find()->where("doc_id=$docId and openid='".$result['fromUsername']."' and user_id=0")->orderBy(['create_time'=>SORT_DESC])->one();
  216. if(empty($downCode))
  217. {
  218. $default_user_group = getDefaultUserGroup(getSysconfigValue('default_user_model'));
  219. if(empty($default_user_group)){
  220. $weixin->responseMsg(array('type'=>'text','content'=>'请设置默认用户组'));
  221. }
  222. $default_user_group_level = getDefaultUserGroupLevel(getSysconfigValue('default_user_model'));
  223. if(empty($default_user_group_level))
  224. {
  225. $weixin->responseMsg(array('type'=>'text','content'=>'请设置默认用户等级'));
  226. }
  227. $userGroupLevel = \app\modules\ucenter\models\UserGroupLevel::findOne($default_user_group_level);
  228. $groupRights = string2array($userGroupLevel->settings);
  229. //日期
  230. $timeList = getTimeList(1);
  231. $todayStart = $timeList['dayStart'];
  232. $todayEnd = $timeList['dayEnd'];
  233. //今日免费下载数量
  234. $freeDownNumToday = DocDowncode::find()->where("openid='".$result['fromUsername']."' and user_id=0 and (create_time>=".$todayStart." and create_time<=".$todayEnd.")")->groupBy('doc_id')->count();
  235. //免费总下载数量
  236. $freeDownNumTotal = DocDowncode::find()->where("openid='".$result['fromUsername']."' and user_id=0")->groupBy('doc_id')->count();
  237. //所在用户组被禁止下载
  238. if($groupRights['doc_downnum_day']==-1||$groupRights['doc_downnum_total']==-1)
  239. {
  240. $weixin->responseMsg(array('type'=>'text','content'=>'您已被禁止下载'));
  241. }
  242. //总数量限制检查
  243. if($groupRights['doc_freedownload_totalnum']>0&&$freeDownNumTotal>=$groupRights['doc_freedownload_totalnum'])
  244. {
  245. $weixin->responseMsg(array('type'=>'text','content'=>'您已达到免费文档下载总量上限('.$groupRights['doc_freedownload_totalnum'].'份),无法继续下载'));
  246. }
  247. //每日数量限制检查
  248. if($groupRights['doc_freedownload_num']>0&&$freeDownNumToday>=$groupRights['doc_freedownload_num'])
  249. {
  250. $weixin->responseMsg(array('type'=>'text','content'=>'您已达到今日免费文档下载总量上限('.$groupRights['doc_freedownload_num'].'份/天),无法继续下载'));
  251. }
  252. $downCode = new DocDowncode();
  253. $downCode->doc_id = $doc->id;
  254. $downCode->openid = strval($result['fromUsername']);
  255. $downCode->downcode = strval(getDowncode($doc->id));
  256. $downCode->user_id = 0;
  257. $downCode->doc_user_id = $doc->user_id;
  258. $downCode->is_ad = $doc->is_ad;;
  259. $downCode->type = 1;
  260. $downCode->create_time = TIMESTAMP;
  261. $downCode->save();
  262. }
  263. //输出回复内容
  264. $picurl = $this->_getDowntipImg();
  265. $downloadUrl = SiteUrl::downByLink($downCode->downcode,1);
  266. $articles = array(
  267. array(
  268. 'title'=>$doc->title,
  269. 'description'=>$doc->description,
  270. 'picurl'=>$picurl,
  271. 'url'=>urldecode($downloadUrl)
  272. ),
  273. );
  274. $weixin->responseMsg(array('type'=>'news','count'=>'1'),$articles);
  275. }
  276. }
  277. else if(!empty($result['event'])) //其他事件反馈
  278. {
  279. //$this->logger(var_export($result,true));
  280. //首次关注推送消息
  281. if(Yii::$app->params['mp']['autoreply']&&$result['event']=='subscribe')
  282. {
  283. //自动消息(客服消息)
  284. $customerMsg = \app\modules\plugins\models\MpReply::find()->where("focus=1")->orderBy(['list_order'=>SORT_ASC])->one();
  285. if(!empty($customerMsg))
  286. {
  287. $data = string2array($customerMsg->data);
  288. $weixin->sendCustomermsg($customerMsg->msg_type,$result['fromUsername'],$data);
  289. //$this->logger(var_export($return,true));
  290. }
  291. //再增加一条关注回复
  292. $reply = \app\modules\plugins\models\MpReply::find()->where("focus=1 and id!='".$customerMsg->id."'")->orderBy(['list_order'=>'SORT_ASC'])->one();
  293. if(!empty($reply))
  294. {
  295. $this->sendReply($weixin,$reply);
  296. }
  297. }
  298. }
  299. }
  300. }
  301. //扫码关注公众号二维码登录处理
  302. public function initOpenAuth($weixin,$result,$scene_id)
  303. {
  304. //$this->logger(var_export($result,true));
  305. //如果开启了unionid
  306. if(Yii::$app->params['openauth']['openUnionId']==1)
  307. {
  308. $userInfo = $weixin->getUserInfo($result['fromUsername']);
  309. if($userInfo['unionid'])
  310. {
  311. $openAuth = UserOpenAuth::find()->where("app_unionname='wx' and app_unionid='".$userInfo['unionid']."'")->orderBy(['id'=>SORT_DESC])->one();
  312. }
  313. }
  314. //$this->logger(var_export($openAuth,true));
  315. if(empty($openAuth)){
  316. $openAuth = UserOpenAuth::find()->where("app_uid='".$result['fromUsername']."' and app='mp'")->one();
  317. }
  318. if(empty($openAuth))
  319. {
  320. if(empty($userInfo)) $userInfo = $weixin->getUserInfo($result['fromUsername']);
  321. $openAuth = new UserOpenAuth();
  322. $openAuth->app='mp';
  323. $openAuth->app_uid = strval($result['fromUsername']);
  324. $openAuth->scene_id = strval($scene_id);
  325. $openAuth->user_info = base64_encode(array2string($userInfo));
  326. $openAuth->bind_time = TIMESTAMP;
  327. $openAuth->hash = md5($openAuth->app.$openAuth->app_uid);
  328. $openAuth->app_unionid = $userInfo['unionid']?$userInfo['unionid']:'';
  329. $openAuth->app_unionname = 'wx';
  330. if(!$openAuth->save())
  331. {
  332. $this->logger($openAuth->returnFirstError());
  333. }
  334. }
  335. else
  336. {
  337. $openAuth->app_unionid = $userInfo['unionid']?$userInfo['unionid']:'';
  338. $openAuth->scene_id = strval($scene_id);
  339. $openAuth->save();
  340. if(!$openAuth->save())
  341. {
  342. $this->logger($openAuth->returnFirstError());
  343. }
  344. }
  345. return $openAuth;
  346. }
  347. //回复消息
  348. public function sendReply($weixin,$reply)
  349. {
  350. $data = string2array($reply->data);
  351. if($reply->msg_type=='news')
  352. {
  353. $articles = array(
  354. array(
  355. 'title'=>$data['title'],
  356. 'description'=>$data['description'],
  357. 'picurl'=>$data['picurl'],
  358. 'url'=>$data['url']
  359. ),
  360. );
  361. $weixin->responseMsg(array('type'=>'news','count'=>'1'),$articles);
  362. }
  363. if($reply->msg_type=='text')
  364. {
  365. $weixin->responseMsg(array('type'=>'text','content'=>$data['content']));
  366. }
  367. if($reply->msg_type=='image'||$reply->msg_type=='voice')
  368. {
  369. $weixin->responseMsg(array('type'=>$reply->msg_type,'media_id'=>$data['media_id']));
  370. }
  371. if($reply->msg_type=='video')
  372. {
  373. $weixin->responseMsg(array('type'=>$reply->msg_type,'media_id'=>$data['media_id'],'title'=>$data['title'],
  374. 'description'=>$data['description']));
  375. }
  376. }
  377. //扫码订阅消息通知
  378. public function initMpMsg($weixin,$result,$scene_id)
  379. {
  380. $openAuth = UserOpenAuth::find()->where("app_uid='".$result['fromUsername']."' and app='mpmsg'")->one();
  381. if(empty($openAuth))
  382. {
  383. $userInfo = $weixin->getUserInfo($result['fromUsername']);
  384. $openAuth = new UserOpenAuth();
  385. $openAuth->app = 'mpmsg';
  386. $openAuth->app_uid = strval($result['fromUsername']);
  387. $openAuth->scene_id = strval($scene_id);
  388. $openAuth->user_info = base64_encode(array2string($userInfo));
  389. $openAuth->bind_time = TIMESTAMP;
  390. $openAuth->hash = md5($openAuth->app.$openAuth->app_uid);
  391. $openAuth->app_unionid = $userInfo['unionid']?$userInfo['unionid']:'';
  392. $openAuth->app_unionname = 'mpmsg';
  393. if(!$openAuth->save())
  394. {
  395. $this->logger($openAuth->returnFirstError());
  396. }
  397. }
  398. else
  399. {
  400. $openAuth->scene_id = strval($scene_id);
  401. $openAuth->save();
  402. if(!$openAuth->save())
  403. {
  404. $this->logger($openAuth->returnFirstError());
  405. }
  406. }
  407. }
  408. //获取下载引导封面图
  409. private function _getDowntipImg($type='downtip',$path=0)
  410. {
  411. $picPath = BASE_PATH.'web'.DIRECTORY_SEPARATOR.'upload'.DIRECTORY_SEPARATOR.'system'.DIRECTORY_SEPARATOR;
  412. dir_create($picPath);
  413. $downtipImg = getFileUrl($this->imageconfig[$type]);
  414. $picFile = $picPath.basename($downtipImg);
  415. if(!file_exists($picFile)) file_put_contents($picFile,https_request($downtipImg));
  416. $picurl = APP_URL.'upload/system/'.basename($downtipImg);
  417. if($path)
  418. {
  419. return $picFile;
  420. }
  421. else
  422. {
  423. return $picurl;
  424. }
  425. }
  426. }