MrZyb Always in fear of one's own ignorance
  • 使用测试号进行测试

    • 用ngrok将本地端口进行公网映射

      • mac 下 ngrok 安装及使用

        brew install node
        npm i ngrok -g
        ngrok http 80

        安装完开启ngrok之后,可通过显示的域名来访问到你本地的web目录 ngrok成功开启

      • 安装 node 之后可能出现错误的解决方法:Mac PHP7.1 dyld: Library not loaded

    • 在Thinkphp5中用composer引入EasyWeChat
      • EasyWeChat官方文档,进入到框架的根目录,执行composer updatecomposer require overtrue/wechat:~4.0 -vvv (这时候本地的PHP版本最好是PHP 7.1 不然会有意想不到的开发困难)
    • 设置接口配置信息 传送门:微信测试号管理后台

      • 第一步,编写服务器校验响应代码。
        
        <?php
        namespace app\wechat\controller;
        use app\base\controller\Base;
        use EasyWeChat\Factory;      

      class Server extends Base { public function construct() { parent::construct(); $config = config('wechat'); $this->app = Factory::officialAccount($config); }

      public function index() { $response = $this->app->server->serve(); return $response->send(); } } // config.php中的内容(config('wechat')获取到的) <?php return [ 'wechat' => [ 'app_id' => 'xxxxxxxxxxxxxxx', 'secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxx', 'token' => 'xxxxxx', 'aes_key' => '', // 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名 'response_type' => 'array', 'log' => [ 'level' => 'debug', 'file' => RUNTIME_PATH . 'log/'.date('Ymd').'/wechat_debug.log', ], ], ];

      
      - 第二步:到测试号管理后台填写配置信息并提交配置。注意要`关闭app_debug` 和 `关闭app_trace`,返回多余的信息都会导致配置失败。
      ![配置成功](https://upload-images.jianshu.io/upload_images/8396841-4258cc2c2d346e6a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    • 完成配置以后这个url就是微信服务器和我们开发服务器的唯一的消息接口了,微信的所有消息都会通知到这个接口。
  • 为我们的消息通知接口设置各种事件的处理。

    • 收到的每个消息我们都可以在config的log部分配置的日志文件看到消息类型和消息内容。准备好代码之后进行测试。
    • 代码示例:
      
      <?php
      namespace app\wechat\controller;
      use app\base\controller\Base;
      use EasyWeChat\Factory;   

    class Server extends Base { public function construct() { parent::construct(); $config = config('wechat'); $this->app = Factory::officialAccount($config); }

    public function index() { // 校验开发者微信服务器 // $this->ckServer();exit(); $this->app->server->push(function ($message) { switch ($message['MsgType']) { case 'event': $returnInfo = $this->eventHandler($message['Event']); return $returnInfo; break; case 'text': return '收到文字消息'; break; case 'image': return '收到图片消息'; break; case 'voice': return '收到语音消息'; break; case 'video': return '收到视频消息'; break; case 'location': return '收到坐标消息'; break; case 'link': return '收到链接消息'; break; default: return '收到其它消息'; break; } }); $response = $this->app->server->serve(); return $response->send(); }

    /**

    • 校验开发者服务器 */ private function ckServer() { $response = $this->app->server->serve(); return $response->send(); }

      private function eventHandler($messageEvent) { switch ($messageEvent) { case 'subscribe': return '欢迎订阅'; break; default: return 'event 事件' . $messageEvent; break; } } }

      
      ![测试结果](https://upload-images.jianshu.io/upload_images/8396841-fce62098e4250a8a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  • 设置公众号菜单

    • 在前面的基础上,设置菜单的函数如下
      public function setMenu()
      {
      $buttons = [
          [
              "type" => "click",
              "name" => "menuClick",
              "key"  => "menu_click_test",
          ],
          [
              "name"       => "二级菜单",
              "sub_button" => [
                  [
                      "type" => "view",
                      "name" => "搜索",
                      "url"  => "https://www.google.com.hk/",
                  ],
                  [
                      "type" => "click",
                      "name" => "subMenuClick",
                      "key" => "sub_menu_click_test"
                  ],
              ],
          ],
      ];
      $setRes = $this->app->menu->create($buttons);
      echo json_encode($setRes, JSON_UNESCAPED_UNICODE);
      }
    • 然后在浏览器中访问这个设置菜单的方法 http://www.your_server_host.com/wechat/server/setMenu,显示 {"errcode":0,"errmsg":"ok"} 则为设置成功,这时候进入公众号即可看到我们设置的菜单栏已经出现了。
    • 接下来我们要来设置点击对应按钮时我们要做的处理逻辑,在前面的代码中修改以下逻辑
      public function index()
      {
      // 校验开发者微信服务器
      // $this->ckServer();exit();
      $this->app->server->push(function ($message) {
         switch ($message['MsgType']) {
             case 'event':
                 $messageEvent = $message['Event'];
                 $messageKey = (isset($message['EventKey']) && !empty($message['EventKey'])) ? $message['EventKey'] : '';
                 $returnInfo = $this->eventHandler($messageEvent, $messageKey);
                 return $returnInfo;
                 break;
             case 'text':
                 return str_replace('?', '', $message['Content']) . '!'; // 实现人工智能
                 break;
             case 'image':
      ...这里是前面编写的其他代码...
    • 将原来的eventHandler修改为以下,并添加对应 $messageEventclickfunction menuClickEventHandler()
      
      private function eventHandler($messageEvent, $eventKey)
      {
      $messageEvent = strtolower($messageEvent);
      switch ($messageEvent) {
         case 'subscribe': // 订阅事件
             return '欢迎订阅';
             break;
         case 'unsubscribe': // 取消订阅事件
             // TODO 可以添加日志等其他逻辑
             return '';
             break;
         case 'click': // 处理菜单点击事件
             return $this->menuClickEventHandler($eventKey);
             break;
         default:
             return "{$messageEvent}, {$eventKey}";
             break;
      }
      }

    /**

    • 菜单栏中type为click的按钮才会发送return的消息回去

    • eventKey是设置菜单时type为click的key值 */ private function menuClickEventHandler($eventKey) { switch ($eventKey) { case 'menu_click_test': return '点击了一级菜单的 menuClick 按钮'; break; case 'sub_menu_click_test': return '点击了二级菜单的 subMenuClick 按钮'; break; default: return "嘻嘻"; break; } }

    • 在公众号中进行测试结果如下 测试结果

    • 创建微信二维码并添加处理扫描微信二维码之后的逻辑代码

      • 创建微信二维码,通过输入对应的参数并调用createQrcode接口即可生成二维码

        
        public function createQrcode()
        {
        $content = $this->request->get('content');
        if (empty($content)) return json(['code' => -1, 'message' => 'content must be set']);
        $expire = $this->request->get('expire', 600);
        $type = $this->request->get('type', 'tmp');
        $createRes = $this->genQrcode($type, $content, $expire);
        
        return $createRes ? json(['code' => 1, 'message' => '保存成功']) : json(['code' => -1, 'message' => '保存失败']);
        }

      public function genQrcode($type, $content, $expire=600) { if ($type == 'tmp') { $result = $this->app->qrcode->temporary($content, $expire); } else { $result = $this->app->qrcode->forever($content); } $url = $this->app->qrcode->url($result['ticket']); // 生成的二维码的图片地址 $uploadService = new UploadService; // 这里面检查了文件保存目录,这里我保存到了public目录下的upload目录 $saveFilename = $uploadService->genSaveFilename('genQrcode', 'jpg'); // 生成了一个可用的保存文件名 $imgContent = file_get_contents($url); $saveRes = file_put_contents($saveFilename, $imgContent); // TODO 记录保存图片地址 return $saveRes; }

      - 处理微信扫描二维码之后的逻辑。用户扫描我们生成的二维码之后,微信会通知我们用户扫描了哪个二维码,区分二维码的就是我们生成二维码时的场景值ID。
      ```php
      // a、在我们原有的`eventHandler`中添加一个`case`,微信通知的`MsgType`为`event`,对应的`Event`值即为`SCAN`,`EventKey`即为扫描的二维码的场景值ID。
      ······other code······
      case 'scan': // 处理微信扫描二维码事件
              return $this->scanEventHandler($eventKey);
              break;
      ······other code······
      // b、在scanEventHandler中对具体的二维码场景值ID进行具体的业务逻辑处理
      private function scanEventHandler($eventKey)
      {
       switch ($eventKey) {
           case 'scan_test': // 场景值ID
               return '使用微信扫描了二维码,扫描事件key值为: ' . $eventKey;
               break;
           default:
               return '使用微信扫描了二维码';
               break;
       }
      }
      • 测试结果 扫描未过期二维码结果 扫描已过期二维码结果
    • 获取AccessToken以及部分用户行为操作的代码

      
      public function getAccessToken()
      {
      $accessToken = $this->app->access_token;
      $token = $accessToken->getToken(); // EasyWechat已经帮我们实现了accessToken的缓存问题,我们直接拿来用就行了
      return json($token);
      }

    /**

    • 获取用户列表
    • @return array users */ public function getUserList() { $userList = $this->app->user->list($nextOpenId = null); return json($userList); }

    /**

    • 获取单个用户的信息
    • @param string $openId 用户openId
    • @return array */ public function getUserInfo() { $openId = $this->request->get('openId'); if (empty($openId)) return json(['code' => -1, 'message' => 'openId must be set']); $userInfo = $this->app->user->get($openId); return json($userInfo); }

    /**

    • 获取多个用户的信息
    • @param array $openIds 用户openId,二维数组
    • @return array */ public function getUsersInfo($openIds = []) { if (empty($openIds)) return json(['code' => -1, 'message' => 'openIds must be set']); $users = $this->app->user->select($openIds); return json($users); }

    /**

    • 设置备注
    • @param string $openId 用户openId
    • @param string $remark 备注信息
    • @return boolean */ public function setRemark() { $openId = $this->request->get('openId'); if (empty($openId)) return json(['code' => -1, 'message' => 'openId must be set']); $remark = $this->request->get('remark'); if (empty($remark)) return json(['code' => -1, 'message' => 'remark must be set']); $res = $this->app->user->remark($openId, $remark); return json($res); }

    /**

    • 获取拉黑用户的列表 默认获取全部
    • @param array $beginOpenid
    • @return array */ public function blackUserList() { $res = $this->app->user->blacklist(); return json($res); }

    /**

    • 拉黑用户
    • @param string/array $openIds 用户openId,单个是字符串,多格式数组
    • @return */ public function block() { $openId = $this->request->get('openId'); if (empty($openId)) return json(['code' => -1, 'message' => 'openId must be set']); $res = $this->app->block($openIds); return json($res); }

    /**

    • 取消拉黑用户
    • @param string/array $openIds 用户openId,单个是字符串,多格式数组
    • @return */ public function unblock() { $openId = $this->request->get('openId'); if (empty($openId)) return json(['code' => -1, 'message' => 'openId must be set']); $res = $this->app->unblock($openIds); return json($res); }
0.004178s