• 首頁 > QQ技巧 > 微信公眾號利用客服消息和模板消息實現微信群發

    微信公眾號利用客服消息和模板消息實現微信群發

    時間:2018-10-27 11:11 作者:QQ地帶 我要評論

    1、關于群發接口和消息接口
    關于群發接口
    1.訂閱號每天可以群發消息一條,服務號每月(自然月)四條的群發權限。開發者模式下,可以通過高級群發接口,實現更靈活的群發能力。
    2.注意
    ● 對于認證訂閱號,群發接口每天可成功調用1次,此次群發可選擇發送給全部用戶或某個標簽;
    ● 對于認證服務號雖然開發者使用高級群發接口的每日調用限制為100次,但是用戶每月只能接收4條,無論在公眾平臺網站上,還是使用接口群發,用戶每月只能接收4條群發消息,多于4條的群發將對該用戶發送失敗;
    ● 具備微信支付權限的公眾號,在使用群發接口上傳、群發圖文消息類型時,可使用a標簽加入外鏈;
    關于客服消息和模板消息接口
    當用戶和公眾號產生特定動作的交互時(具體動作列表請見下方說明),微信將會把消息數據推送給開發者,開發者可以在一段時間內(目前修改為48小時)調用客服接口,通過POST一個JSON數據包來發送消息給普通用戶。此接口主要用于客服等有人工消息處理環節的功能,方便開發者為用戶提供更加優質的服務。
    模板消息僅用于公眾號向用戶發送重要的服務通知,只能用于符合其要求的服務場景中,如信用卡刷卡通知,商品購買成功通知等。不支持廣告等營銷類消息以及其它所有可能對用戶造成騷擾的消息。
     
    2、背景
    簡單的描述一下業務的背景,目前是做一個供求的微信公眾號,每當用戶付費發送一次供求的信息,我們需要將本條消息推送給所有的用戶,這樣可以讓用戶及時收到消息,實現消息的實時性和保證消息的有效性。但是群發的接口根本不能滿足我們的需求,于是我們利用模板消息和客服消息的接口來實現我們的需求,在此,有的人可能會發問,為什么客服消息的接收要求這么變態還要利用它呢,其實是因為,群發的消息在某些設備上收到的時候,就像微信好友發送的一條信息,更加吸引用戶去關注。
    另外,我們還需要知道,假如我們的用戶用成千上萬的人話,那么群發的方式是十分的耗時的,微信支付提供了notify的異步通知,之前我也一直嘗試利用這個現場的異步通知來實現群發,但是根據實際的使用,這個異步IO的閾值差不多在6分鐘左右,可是實際上發送一次模板的網絡耗時加上數據庫的IO其實還是時間還是挺長的,因此我們不得不使用異步多線程來實現我們的群發的功能這里我利用的是swoole的擴展來完成這一需求的。
    swoole的異步郵件群發的demo,大家可以參照我的另一篇文章thinkphp5+swoole實現異步郵件群發(SMTP方式)這里可以較為詳細的了解服務端和客戶端的構建。
     
    3、環境說明
    centos7
    swoole2.0+
    tp5.0+
    郵件發送
    crontab定時任務
     
    4、實現
    4.1創建服務端
    我這里就直接貼代碼了,需要注意的地方都寫在了相關代碼的注釋,注意看一下
     
    /**
         * description:服務端
         */
        public function asyncSend()
        {
            $serv = new \swoole_server('0.0.0.0', 9090);
            $serv->set(array(
                'task_worker_num' => 60,
                'daemonize' => true,
                'log_file' => '/var/www/html/myswl/tp/swoole.log'
                )
            );
            $serv->on('receive', function ($serv, $fd, $from_id, $data) {
                $task_id = $serv->task($data);
                echo "開始投遞異步任務 id=$task_id\n";
            });
     
            $serv->on('task', function ($serv, $task_id, $from_id, $data) {
                echo "接收異步任務[id=$task_id]" . PHP_EOL;
                $data = json_decode($data,true);
     
    //這里要特別的說明,我這里用到了數據庫的遠程鏈接,因為支付的功能在另一臺window虛擬云主機上的,所以不得不利用遠程訪問的方式。Db::connect($conn,true);的第二個參數給給予關注,因為我們沒發送一次其實都會去進行一次遠程數據庫連接,所以頻繁的連接中,肯定會有連接失敗的情況,因此我們需要做好斷線重連的配置
                $conn = 'mysql://用戶名:數據庫遠程鏈接地址:3306/密碼#utf8';
                $db = Db::connect($conn,true);
     
                $now = date('Y-m-d H:i:s');
                $users = $db
                    ->table('tp_receiver')
                    ->field('openid')
                    ->where("(`expire_time` > '{$now}' OR `send_status` = 1 ) AND `receive_status` = 1 ")
                    ->limit($data['flag']*500,500)
                    ->select();
     
                echo $db->getLastSql();
     
                echo 'send start--' . date('H:i:s') . PHP_EOL;
                foreach ($users as $user) {
                    $token = $db->table('tp_accesstoken')->field('accesstoken')->find();
    //這里是客服消息
                    $templData2 = array(
                        'touser' => $user['openid'],
                        'msgtype' => 'text',
                         'text' => array('content' => $data['content']."\n\n點擊下方“信息查詢”查看更多求購信息。"."\n回復0取消接收信息,回復1重新接收信息")
                    );
     
                    $res = $this->sendCustomMessage($templData2,$token['accesstoken']);
                    $res = json_decode($res, true);
    //判斷客服消息是否成功發送
                    if($res['errcode'] == 45047 || $res['errcode'] == 45015) {
                        $templData = array(
                            'touser' => $user['openid'],
                            'template_id' => '模板消息',
                            'url' => '',
                            'data' => array(
                                'first' => array('value' => '您好,您收到一條新的提醒', 'color' => '#173177'),
                                'keyword1' => array('value' => '求購信息', 'color' => '#173177'),
                                'keyword2' => array('value' => $data['content'], 'color' => '#173177'),
                                'keyword3' => array('value' => $data['phone'], 'color' => '#173177'),
                                'keyword4' => array('value' => date('Y-m-d H:i:s'), 'color' => '#173177'),
                                'remark' => array('value' => "點擊下方“信息查詢”查看更多求購信息。"."回復0取消接收信息,回復1重新接收信息", 'color' => '#173177')
                            ),
                        );
                        $res = $this->sendTemplateMessage($templData, $token['accesstoken']);
                        Log::write('send to'.$user['openid'].$res . PHP_EOL);
                    }
                }
                echo 'send end--' . date('H:i:s') . PHP_EOL;
     
                $serv->finish('');
            });
     
            $serv->on('finish', function ($serv, $task_id, $data) {
                echo 'finish time--' . microtime(true) . PHP_EOL;
                echo "異步任務[id=$task_id]完成" . PHP_EOL;
            });
     
            $serv->start();
        }
    然后我們在CLI模式下進入項目的根目錄,執行
     
    php public/index.php demo/wechat/asyncSend
    這樣我們的服務端就以守護進程的模式一直運行來我們的后臺了,通過ps -aux | grep asyncSend
    可以看見,已經有62個進程在處于S(睡眠待喚醒)的狀態了,除了60個task進程還用一個master和一個woker進程。
     
    process.png
     
    4.2構建客戶端
    代碼如下,需要注意的地方都寫在了相關代碼的注釋,注意看一下
     
    /**
         * description:客戶端
         */
        public function index()
        {
    //因為這個群發比較敏感,我們需要做一個token的機制,我這邊就用最簡單的發送方和接收方都以明文的方式來做了。
                $token = $_GET['token'];
                if($token != 'test'){
                    exit;
                }
    //content是發送的內容,因為不可預估里面的東西,所以進行加解密
                $content = rawurldecode($_GET['content']);
                $flag = $_GET['flag'];
                $id = $_GET['id'];
                $phone = $_GET['phone'];
     
                $data['content'] = $content;
                $data['flag'] = $flag;
                $data['phone'] = $phone;
     
                Log::write(self::json_encode($data));
     
                $insert = [
                    'flag'=>$flag,
                    'miaomu_id'=>$id,
                    'status'=>1,
                    'createtime'=>date('Y-m-d H:i:s'),
                    'content'=>$content
                ];
                Db::table('task')->insert($insert);
     
    //異步客戶端
                $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
                $ret = $client->connect("127.0.0.1", 9090);
    //當無法連接的時候,發送告警郵件
                if (empty($ret)) {
                    SendMail::postmail('[email protected]','警告','error!connect to swoole_server failed');
                    SendMail::postmail('[email protected]','警告','error!connect to swoole_server failed');
                    Log::write('error!connect to swoole_server failed');
                } else {
                    $client->send(self::json_encode($data));
                }
        }
    4.3端口監控
    這個群發已經涉及到金額了,所以我們更要關系服務的運行穩定了,這里我們簡單的利用crontab定時任務和Php的一些shell相關函數來實現端口的監控。
    本次用到的定時任務
     
    */1 * * * * curl http://你的域名/index.php/demo/Jrmm/checkPortStatus?token=test
    就是實現一個每分鐘去執行我們下面php代碼的一個任務,這里我沒有直接用shell來操作,原因有3點,1是我不是很熟悉shell命令,2是我們不太熟悉shell命令,3是寫在php里面更方便我去寫相關代碼和利用已有的一些方法,比如郵件發送。這樣雖然多了一點網絡資源的消耗,但是也還劃算。
    具體的監控代碼,這邊實現的時候會出現很多權限的問題,我就不多說了,遇到的時候自行百度。
    /**
     
     * description:8082服務端口監控
     */
    public function checkPortStatus(){
        if (!isset($_GET['token']) || $_GET['token'] != 'test'){
            exit();
        }
        $res1 = exec('sudo netstat -lpn | grep 9090');
        Log::write($res1);
        if($res1 == ''){
            Log::write('9090stop');
            SendMail::postmail('[email protected]','警告','9090端口服務錯誤');
            SendMail::postmail('[email protected]','警告','9090端口服務錯誤');
    //重啟我們的服務端,這里需要注意的是,我沒有用到swoole提供的平滑重啟的功能,很可能會造成數據的丟失,這別額外的需要注意
     
            exec('sudo php /var/www/html/myswl/tp/public/index.php demo/jrmm/asyncSend');
            $res = exec('sudo netstat -lpn | grep 8082');
            if($res != ''){
                Log::write('9090restart success');
                SendMail::postmail('[email protected]','警告解除','9090端口重啟成功');
                SendMail::postmail('[email protected]','警告解除','9090端口重啟成功');
            }
        }
    }
    4.4實現
    我們利用PHP curl函數來模擬一次支付成功后調用我們群發的功能。
     
    $content = 'test';
            for ($j=0;$j<3;$j++){
                 $url = 'http://你的域名/index.php/demo/Jrmm/index'.'?flag='.$j.'&id=1.'&token=test'.'&phone='110&content='.rawurlencode($content);
                $this->http_post($url);
            }
    function http_post($url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        $res= curl_exec($ch);
        curl_close($ch);
        return $res;
    }
    為什么循環三次呢,因為我們通過測試發送發送1500次消息的時候,耗時差不多6分鐘,但是我們的項目的并發很低,那么就無法充分利用我們開啟的60個task進程,所以我們將1500分成三次去發送那么實際上我們消耗了幾乎可以忽略不計的網絡消耗,讓我們的發送的性能提高了三倍多,實際的項目中,發送1500多條實際耗時只要不到兩分鐘。當然當并發量更大的時候,我們還可以采用隊列的方式來處理,這樣需要我們隊task進程管理更加的熟練。

    標簽: 微信公眾號
    頂一下
    (0)
    0%
    踩一下
    (0)
    0%

    Google提供的廣告

    pk10最牛稳赚计划软件
  • 体彩p3藏机图汇总大全 时时彩玩单双计划 湖北快三历史开奖记录 内幕凤凰四肖中特→免费公开 河北快三形态走势图 11选5杀2个100%技巧 贵州风光诗词 时时彩走势图龙虎和 排列三跨度走势图彩经网 国际品牌棒球帽排行榜 极速11选5计划网 河北快3任意两位 香港马会一肖公式规律 海南博彩 秒速时时彩计划公式