123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- <?php
- namespace app\admin\library;
- use app\common\library\Rabbitmq;
- use app\common\library\Redis;
- use app\common\library\WeChatObject;
- use app\common\utility\WeChatMenu;
- use app\main\service\WeChatAdService;
- use GuzzleHttp\Client;
- use Symfony\Component\Cache\Simple\RedisCache;
- use EasyWeChat\Factory;
- use think\Config;
- use think\Log;
- class WeChatAuthorization{
- const SYNC_MQ_QUEUE_NAME = 'Q_SYNC_USER';
- const SYNC_MQ_EXCHANGE_NAME = 'E_SYNC_USER';
- protected $platform;
- protected $adminConfig;
- protected $weChat;
- public function __construct($platform_id,$channel_id){
- if(!$platform_id || !$channel_id){
- throw new \Exception('Platform_id OR Channel_id 为空');
- }
- //获取平台信息
- if(!$this->platform = model('Platform')->where(['id'=>$platform_id])->find()){
- throw new \Exception('获取平台信息失败');
- }
- Log::info(sprintf('微信授权: platform_id:%d channel_id:%d 取得平台信息',$platform_id,$channel_id));
- //获取渠道信息
- if(!$this->adminConfig = model('AdminConfig')->where(['admin_id'=>$channel_id])->find()){
- throw new \Exception('获取用户信息失败');
- }
- Log::info(sprintf('微信授权: platform_id:%d channel_id:%d 取得渠道信息',$platform_id,$channel_id));
- $wechatObj = new WeChatObject($this->adminConfig);
- $this->weChat = $wechatObj->getPlatform($platform_id);
- }
- /**
- * 授权处理
- * @param bool $isUpdate 是否是更新权限
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\ModelNotFoundException
- * @throws \think\exception\DbException
- */
- public function authorization($isUpdate = false){
- // 使用授权码换取接口调用凭据和授权信息
- $auth = $this->weChat->handleAuthorize();
- // 检查当前授权的公众号与历史授权公众号是否一致
- $existsAppID = model('AdminConfig')->where('appid',$auth['authorization_info']['authorizer_appid'])->where('admin_id','neq',$this->adminConfig->admin_id)->find();
- if($existsAppID){
- //更新平台用户refreshToken信息
- $this->updateUserRefrshToken($this->platform->id,$auth['authorization_info']['authorizer_refresh_token'],$existsAppID['admin_id']);
- throw new \Exception('一个服务号只能绑定一个账号!');
- }
- if ($this->adminConfig->appid && $this->adminConfig->appid != $auth['authorization_info']['authorizer_appid']) {
- throw new \Exception('当前授权的公众号与历史授权公众号不一致!');
- }
- // 获取授权方的帐号基本信息
- $authInfo = $this->weChat->getAuthorizer($auth['authorization_info']['authorizer_appid']);
- Log::info(sprintf('微信授权: platform_id:%d channel_id:%d 微信授权检测通过',$this->platform['id'],$this->adminConfig['admin_id']));
- //检测权限
- $this->checkWeChatAuth($authInfo);
- //更新平台用户refreshToken信息
- $this->updateUserRefrshToken($this->platform->id,$auth['authorization_info']['authorizer_refresh_token']);
- $data['appid'] = $auth['authorization_info']['authorizer_appid'];
- $data['json'] = $authInfo;
- if(!$this->adminConfig->save($data)){
- throw new \Exception('更新授权信息失败');
- }
- Log::info(sprintf('微信授权: platform_id:%d channel_id:%d 微信基本信息更新完成',$this->platform['id'],$this->adminConfig['admin_id']));
- //检查默认平台
- if($this->platform['id'] == $this->adminConfig['platform_id'] && !$isUpdate){
- try {
- //同步微信用户
- $this->syncWeChatUsers($this->adminConfig['admin_id'],$auth['authorization_info']['authorizer_appid']);
- $menu = [];
- //检测是否是单独配置菜单域名
- if(isset($this->adminConfig['menuophost_id']) && $this->adminConfig['menuophost_id']){
- if($ophost = model('Ophost')->where('id',$this->adminConfig['menuophost_id'])->find()){
- $menu = json_decode(str_replace('cpsurl', config('site.scheme'). '://' .$auth['authorization_info']['authorizer_appid'].'.'.$ophost['host'], config('site.wx_menu')), true);
- }
- }else{
- $menu = json_decode(config('site.wx_menu'), true);
- foreach($menu as $key => $val){
- if(isset($val['url'])){
- $menu[$key]['url'] = $this->getWeChatMenuUrl($this->platform->id,$auth['authorization_info']['authorizer_appid'],$val['url']);
- }
- if(isset($val['sub_button']) && !empty($val['sub_button'])){
- foreach($val['sub_button'] as $sub_k => $sub_v){
- if(isset($sub_v['url'])){
- $menu[$key]['sub_button'][$sub_k]['url'] = $this->getWeChatMenuUrl($this->platform->id,$auth['authorization_info']['authorizer_appid'],$sub_v['url']);
- }
- }
- }
- }
- }
- //默认平台时更新菜单
- $data['wx_menu'] = $this->updateUserWeChatMenu($auth['authorization_info']['authorizer_appid'],
- $auth['authorization_info']['authorizer_refresh_token'],$menu);
- if (!$this->adminConfig->save($data)) {
- Log::error('授权时,更新微信菜单失败2');
- }
- Log::info(sprintf('微信授权: platform_id:%d channel_id:%d 微信菜单数据更新完成',$this->platform['id'],$this->adminConfig['admin_id']));
- }catch (\Exception $exception){
- Log::notice($exception->getMessage());
- Log::notice('授权时,更新微信菜单失败1');
- }
- }
- model('AdminConfig')->delAdminInfoAllCache($this->adminConfig->admin_id);
- //添加微信AD数据源
- WeChatAdService::instance()->createChannelWxAdSourceId($this->adminConfig['admin_id']);
- //清除缓存
- $redis = Redis::instance();
- $redis->del('AA:' . $auth['authorization_info']['authorizer_appid']);
- }
- /**
- * 更新用户微信菜单
- * @param $app_id
- * @param $refresh_token
- * @param $menu
- * @return bool|mixed
- */
- private function updateUserWeChatMenu($app_id,$refresh_token,$menu){
- //获取用户公众号
- $officialAccount = $this->weChat->officialAccount($app_id, $refresh_token);
- $officialAccount->http_client = new Client(Config::get('wechat.http'));
- $officialAccount['cache'] = new RedisCache(Redis::instanceCache());
- $officialAccount->menu->delete(); //删除全部菜单 主要是掌中云个 性化菜单
- $ret = $officialAccount->menu->create($menu);
- if (!$ret || $ret['errcode'] != 0) {
- return null;
- }else{
- $menu = WeChatMenu::rechargeMenuFilter($menu);
- if(!empty($menu)){
- $ret = $officialAccount->menu->create($menu,['client_platform_type' => 1]);
- }
- }
- if (!$ret || (isset($ret['errcode']) && $ret['errcode'] != 0)) {
- return null;
- }
- Log::info(sprintf('微信授权: platform_id:%d channel_id:%d 微信菜单更新完成',$this->platform['id'],$this->adminConfig['admin_id']));
- return $menu;
- }
- /**
- * 获取菜单URL
- * @param $platform_id
- * @param $app_id
- * @param $redirect
- * @return string
- */
- private function getWeChatMenuUrl($platform_id,$app_id,$redirect){
- $redirect = str_replace('cpsurl', '', $redirect);
- $webParams = [];
- $entryHost = model('Entryhost')->getInfo();
- if($entryHost){
- $webSite = config('site.scheme'). '://' .$entryHost['host'];
- $webParams['channel_id'] = $this->adminConfig->admin_id;
- }else{
- if($ophost = model('Ophost')->getInfo($platform_id,$this->adminConfig->ophost_id)){
- $webSite = \think\Config::get('site.scheme').'://'.$app_id.'.'.$ophost['host'];
- }else{
- $webSite = '';
- }
- }
- if($redirect){
- $pathParams = parse_url($redirect);
- //添加路径
- if(isset($pathParams['path']) && $pathParams['path']){
- $webSite .= $pathParams['path'];
- }
- //检查路径是否有参数
- if(isset($pathParams['query']) && $pathParams['query']){
- $pathQuery = explode('&',$pathParams['query']);
- if($pathQuery){
- foreach($pathQuery as $val){
- $temp = explode('=',$val);
- $webParams[$temp[0]] = $temp[1] ?? '';
- }
- }
- }
- }
- if($webParams){
- $webSite .= '?'.http_build_query($webParams);
- }
- return $webSite;
- }
- /**
- * 更新用户RefrshToken
- * @param $platform_id
- * @param $refresh_token
- * @param $admin_id
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\ModelNotFoundException
- * @throws \think\exception\DbException
- * @throws \Exception
- */
- private function updateUserRefrshToken($platform_id,$refresh_token,$admin_id = null){
- //更新ptoken信息
- $ptoken_map = ['admin_id'=>$this->adminConfig->admin_id,'platform_id'=>$platform_id];
- if($admin_id){
- $ptoken_map['admin_id'] = $admin_id;
- }
- if($ptoken = model('Ptoken')->where($ptoken_map)->find()){
- if(false === model('Ptoken')->where($ptoken_map)->update(['refresh_token'=>$refresh_token,'updatetime'=>time()])){
- throw new \Exception('更新RefrshToken失败');
- }
- }else{
- $insert_data = array_merge($ptoken_map,['refresh_token' => $refresh_token,'createtime'=>time(),'updatetime'=>time()]);
- if(!model('Ptoken')->insert($insert_data)){
- throw new \Exception('写入RefrshToken失败');
- }
- }
- Log::info(sprintf('微信授权: platform_id:%d channel_id:%d 微信PTOKEN更新完成',$this->platform['id'],$this->adminConfig['admin_id']));
- }
- /**
- * 检测权限
- * @param $authUser
- * @throws \Exception
- */
- private function checkWeChatAuth($authUser){
- if (!isset($authUser['authorizer_info']['service_type_info']['id'])) {
- throw new \Exception('获取授权信息失败,请重新扫码授权');
- }
- if ($authUser['authorizer_info']['service_type_info']['id'] != 2) {
- throw new \Exception('当前授权公众号类型非服务号,请使用服务号进行扫码');
- }
- if ($authUser['authorizer_info']['verify_type_info']['id'] == -1) {
- throw new \Exception('当前授权服务号未认证,请使用认证服务号进行扫码');
- }
- $authList = array_column(array_column($authUser['authorization_info']['func_info'], 'funcscope_category'), 'id');
- if(empty($authList)){
- throw new \Exception('获取授权信息失败');
- }
- if(!in_array(1,$authList)){
- throw new \Exception('必须授予-消息管理权限');
- }
- if(!in_array(15,$authList)){
- throw new \Exception('必须授予-自定义菜单权限');
- }
- if(!in_array(4,$authList)){
- throw new \Exception('必须授予-网页服务权限');
- }
- if(!in_array(2,$authList)){
- throw new \Exception('必须授予-用户管理权限');
- }
- if(!in_array(3,$authList)){
- throw new \Exception('必须授予-帐号服务权限');
- }
- if(!in_array(11,$authList)){
- throw new \Exception('必须授予-素材管理权限');
- }
- if(!in_array(23,$authList)){
- throw new \Exception('必须授予-广告管理权限');
- }
- }
- private function syncWeChatUsers($channel_id,$appid){
- if(!model('SyncUser')->where(['admin_id'=>$channel_id,'appid'=>$appid])->find()){
- $mq = new Rabbitmq();
- $mq->send(['channel_id'=>$channel_id,'appid'=>$appid],self::SYNC_MQ_QUEUE_NAME,self::SYNC_MQ_EXCHANGE_NAME);
- Log::info('syncUser->Queue: Channel_id:'.$channel_id.' AppID:'.$appid.' 添加微信用户同步任务');
- }else{
- Log::info('syncUser->Queue: Channel_id:'.$channel_id.' AppID:'.$appid.' 已同步过微信用户');
- }
- }
- }
|