valid(); // 事件处理 $this->handleEvent(); } /** * 开发者用来接收微信消息和事件的入口函数 * 对应公众号管理后台: 开发-基本设置页面填写的服务器地址 */ public function handleEvent() { $wechat = WxHelper::getWxPHPSDK(); $revType = $wechat->getRev()->getRevType(); $revFromOpenId = $wechat->getRev()->getRevFrom(); switch($revType) { case WxPHPSDK::MSGTYPE_TEXT: // 文本 $keyword = trim($wechat->getRevContent()); if (YII_ENV_TEST) { // 关注公众号 if ($keyword == "welcome") { $title = isset($this->wx->subscribe_title) ? $this->wx->subscribe_title : '欢迎关注极办公'; $description = isset($this->wx->subscribe_desc) ? $this->wx->subscribe_desc : $title; $picUrl = $this->wx->subscribe_img ? ImageManager::getUrl($this->wx->subscribe_img) : false; $message = array(array( 'title' => $title, 'description' => $description, 'picurl' => $picUrl, 'url' => WxHelper::getDomain(true) )); WechatMessageHelper::sendCustomerNewsMessage($revFromOpenId, $message, $wechat); return ""; } // 开启短信消息测试 SmsMessage::test($keyword); } break; case WxPHPSDK::MSGTYPE_EVENT: // 事件 $receiveEvent = $wechat->getRev()->getRevEvent();//获取接收事件推送 $event = $receiveEvent["event"]; $revSceneId = $wechat->getRev()->getRevSceneId(); AppLog::DEBUG("微信事件: event=" . $event); switch ($event){ case "subscribe": // 关注公众号 { AppLog::DEBUG("微信关注事件: Openid=[" . $revFromOpenId . "]"); $title = isset($this->wx->subscribe_title) ? $this->wx->subscribe_title : '欢迎来到极办公'; $description = isset($this->wx->subscribe_desc) ? $this->wx->subscribe_desc : $title; $picUrl = $this->wx->subscribe_img ? ImageManager::getUrl($this->wx->subscribe_img) : false; $linkUrl = isset($this->wx->subscribe_url) ? $this->wx->subscribe_url : WxHelper::getDomain(true) . "/user"; $message = array(array( 'title' => $title, 'description' => $description, 'picurl' => $picUrl, 'url' => $linkUrl )); // 推送关注图文消息 WechatMessageHelper::sendCustomerNewsMessage($revFromOpenId, $message, $wechat); // 关注后处理 $this->autoSaveFromWechat($wechat, $revFromOpenId,true); break; } case "unsubscribe": // 取消关注 { // 更新数据表中的关注状态 $userModel = UserRepository::findOneByOpenId($revFromOpenId); if ($userModel) { $userModel->subscribe = false; // is_enable_withdraw 是否要关闭了? if( false == $userModel->save()){ throw new Exception(implode("\r\n", $userModel->getFirstErrors())); } AppLog::DEBUG("取消关注: openid=" . $revFromOpenId); } break; } case "CLICK": // 点击微信菜单 $menuUrl = $receiveEvent["key"]; AppLog::DEBUG("微信点击菜单: menuUrl=[" . $menuUrl . "]"); break; case "SCAN": // 扫描二维码 break; case "LOCATION": // 获取位置 $location = $wechat->getRev()->getRevEventGeo(); // 这里最好判断一下是不是认证过的工程师才能更新地理位置 AppLog::DEBUG("获取工程师地理位置:" . $revFromOpenId.' X->'.$location['x'].' Y->'.$location['y']); break; case 'KEY_INDEX' : break; case 'KEY_GROWTH' : break; default: break; } break; case WxPHPSDK::MSGTYPE_IMAGE: // 图像 break; case WxPHPSDK::MSGTYPE_LOCATION: //获取接收地理位置 $geoArr=$wechat->getRev()->getRevGeo(); AppLog::DEBUG("获取接收地理位置:" . $revFromOpenId.' geoArr->'.json_encode($geoArr)); break; case "TEMPLATESENDJOBFINISH": // 模板消息发送结束 break; default: break; } } /** * 桥接两个用户 * @return \yii\web\Response */ public function actionBridging() { $sn = $this->request->get('sn'); $tourl = $this->request->get('tourl'); $tourl = urldecode($tourl); //如果当前用户已经是注册用户,那么跳过本步骤。 //利用session存储跳转信息 $appUser = Yii::$app->getUser(); if($appUser->isGuest){ //AppLog::DEBUG("绑定用户"); $session = Yii::$app->session; $session->set('oauth_return_url', urlencode($tourl)); $session->set('sn', urlencode($sn)); $redirectUri = Yii::$app->request->getHostInfo() . Url::to(['/wechat/login-proccess', 'dest' => $tourl]); $wechat = WxHelper::getWxPHPSDK(); $OAuthUrl = $wechat->getOAuthRedirect($redirectUri, 'jiwork', 'snsapi_base'); return $this->redirect($OAuthUrl); } else { AppLog::DEBUG(" actionBridging 直接跳转"); return $this->redirect($tourl); } } /**注销登录 * @return \yii\web\Response */ public function actionLogout() { if(Yii::$app->getUser()->logout()){ return $this->goHome(); } } /** * 检测到微信用户未登录, 马上执行登录处理 */ public function actionLoginProccess() { AppLog::DEBUG("actionLoginProccess: 执行登录处理"); /** * 如果用户点击了确认授权, * 那么返回的链接GET中就会有 code */ if($code = Yii::$app->request->get('code')){ $wechat = WxHelper::getWxPHPSDK(); /* * $tokenResult的结果: * { * "access_token":"OezXcEiiBSKSxW0eoylIeAQ5V9CwnswVLKYv6D2nT4StW1c8B7QwjUNokVDLAC2_yxF5nAkAUCf1ciogRmyJu2ERvGvsbLtMO0tbfkkHZ6u5QYXZNArn3k7n_7TIUrnuGWHY-zXyz0W_Kce3ls6Dzw", * "expires_in":7200, * "refresh_token":"OezXcEiiBSKSxW0eoylIeAQ5V9CwnswVLKYv6D2nT4StW1c8B7QwjUNokVDLAC2_-ao_OHlsaK1DBXCCl2OXQgAFvraBXWjxAikuV3jpvkX528za96t2XJ4r3Igs37tYO5JMXCiOQ2jSEVDT1J4wpg", * "openid":"oicCewi22xTYCclqDOVEP-g-uawY", * "scope":"snsapi_base" * } */ $redis = Yii::$app->redis; $storeKey = 'ACCESS_TOKEN_' . $code; if ($redis->exists($storeKey)) { // 已成功获取token结果 $tokenResult = $redis->get($storeKey); $tokenResult = json_decode($tokenResult, true); } else { $tokenResult = $wechat->getOAuthAccessToken($code);//用户授权的 access token if($wechat->errCode){ AppLog::ERROR("{$wechat->errCode} {$wechat->errMsg} ".json_encode($tokenResult)); return; } if(false == $tokenResult){ AppLog::ERROR("get token result fail."); return; } // 保存token结果 $redis->set($storeKey, json_encode($tokenResult)); $redis->expireat($storeKey, time() + 30); // 到期自动删除 } $this->processWechatLogin($wechat, $tokenResult['openid']); } } /** * 处理微信用户逻辑 * @param WxPHPSDK $wechat * @param array $tokenResult * @return bool|void */ protected function processWechatLogin(WxPHPSDK $wechat, $openId) { $userEntity = $this->autoSaveFromWechat($wechat, $openId); if(!($userEntity instanceof UserModel)) { AppLog::debug("processWeChatLogin: 返回 UserEntity 失败"); } /** * 登陆时间为7000秒,目前微信API的access token 的 expires_in 为 7200秒 */ if(Yii::$app->getUser()->login($userEntity, 7000)) { $session = Yii::$app->session; $returnUrl = urldecode($session->get('oauth_return_url')); $session->remove('oauth_return_url'); if ($returnUrl) { return $this->redirect($returnUrl); } $jumpDestUrl = $this->request->get('dest'); if ($jumpDestUrl) { // 判断URL访问路径 $arr = parse_url($jumpDestUrl); AppLog::DEBUG("jumpDestUrl:".$jumpDestUrl); if (isset($arr['query']) && !empty($arr['query'])) { $arr_query = self::convertUrlQuery($arr['query']); if (isset($arr_query["jumppath"]) && isset($arr_query["jumpmodel"]) && !empty($arr_query["jumppath"]) && !empty($arr_query["jumpmodel"])) { AppLog::DEBUG("processWechatLogin:arr_query(path=".$arr_query["jumppath"].",model=".$arr_query["jumpmodel"].")"); $locationUrl = Yii::$app->params["baseUrl"]."/".$arr_query["jumpmodel"]."#".$arr_query["jumppath"]; header("Location: $locationUrl");exit; } } AppLog::DEBUG("processWechatLogin登录成功: 目标跳转=" . $jumpDestUrl); return $this->redirect($jumpDestUrl); } return $this->goHome(); } } /** * 将字符串参数变为数组 * @param $query * @return array array (size=3) * 'm' => string 'content' (length=7) * 'c' => string 'index' (length=5) * 'a' => string 'lists' (length=5) */ function convertUrlQuery($query) { $queryParts = explode('&', $query); $params = array(); foreach ($queryParts as $param) { $item = explode('=', $param); $params[$item[0]] = $item[1]; } return $params; } private function saveProfile($userModel, $userinfoResult) { if(isset($userinfoResult['nickname'])){ $userModel->nickname = $userinfoResult['nickname']; $userModel->gender = $userinfoResult['sex']; $userModel->province = $userinfoResult['province']; $userModel->city = $userinfoResult['city']; $userModel->country = $userinfoResult['country']; $userModel->headimgurl = $userinfoResult['headimgurl']; AppLog::DEBUG('User saveProfile User id : '.$userModel->id); if(false == $userModel->save()){ throw new Exception(implode("\r\n", $userModel->getFirstErrors())); } } } /** * 公众号处理逻辑 * @param WxPHPSDK $wechat * @param $openId */ private function autoSaveFromWechat(WxPHPSDK $wechat, $openId,$isSubscribe = false) { $userinfoResult = $wechat->getUserInfo($openId); AppLog::DEBUG("autoSaveFromWechat: 用户[{$openId}]读取微信资料[" . json_encode($userinfoResult) . ']'); if($wechat->errCode){ AppLog::ERROR("{$wechat->errCode} {$wechat->errMsg} ".json_encode($userinfoResult)); return false; } /** * 通过 openid 来查找用户, * 一个用户在一个公众号的 open id 是唯一的,无论是否关注过。 * 如果存在,那么从数据库中拿出来。 * 如果不存在,新建一个实例。 */ $userModel = UserRepository::findOneByOpenId($openId); /** * 保存 union id */ $unionid = 0; if( isset( $userinfoResult['unionid'] ) ){ $unionid = $userinfoResult['unionid']; } if( false == $userModel ){ $userModel = new UserModel; // 保存openId $userModel->openid = $openId; } if( !empty($unionid) ) { $userModel->unionid = $unionid ; } // 如果已关注 $userModel->subscribe = (bool)$userinfoResult['subscribe']; $userModel->last_login_at = time(); $transaction = Yii::$app->db->beginTransaction(); try{ /** * 如果当前用户是第一次访问,新记录。 */ if( true == $userModel->isNewRecord ) { if(!$userModel->save()){ throw new Exception(implode("\r\n", $userModel->getFirstErrors())); } $newEngineerId = $userModel->attributes['id']; } else { if ( false == $userModel->save() ) { throw new Exception(implode("\r\n", $userModel->getFirstErrors())); } } // 更新profile信息 $this->saveProfile($userModel, $userinfoResult); $transaction->commit(); return $userModel; } catch (Exception $exception) { AppErrorLog::error('autoSaveFromWechat exception openid '. $openId .' message:'.$exception->getMessage()); $transaction->rollBack(); return null; } } }