微信模板消息发送(修改版)

前几天写了个微信模板发送的功能,但后来测试的时候发现存在一些严重的错误,修复过后,客户又加了个上传Excel表格获取用户openid发送信息的功能,由于之前没接触过,还是挺迷茫的,好在经过加班加点之后,终于搞定了这个功能。以下只是简略版本,重点实现功能,各位看官将就看一下。

//使用此代码前需做以下准备工作:
//1、建立一个名为access_token.php的空php文件
//2、在微信公众平台使用管理员权限设置好ip白名单,并在功能设置中绑定业务域名(此文件所在的网络地址),记得根据提示下载MP_verify_TcfRJJNVAMluRK8C.txt文件放在同级目录下
//3、在数据库中建立一个名为send的表,表中包含id、openid、nickname、groupid字段
//4、建立一个名为log.php的空php文件
//5、下载PHPExcel 1.8版本
//6、建立一个名为next_openid.php的空PHP文件
//7、建立一个名为upload的空文件夹,内放一个名为office的空文件夹
//8、下载layui框架
//9、下载jquery 1.8或以上版本

//本页共有两个PHP文件,send.php为主要文件,upload.php为上传文件,可按需截取

 

 

 

send.php 

<meta charset="utf-8">

<?php
require("PHPExcel-1.8/Classes/PHPExcel.php"); //引入PHPExcel

//数据库封装类(在网上找的,随便改动了一下)
class MyPDO
{
    protected static $_instance = null;
    protected $dbName = '';
    protected $dsn;
    protected $dbh;

    /**
     * 构造
     *
     * @return MyPDO
     */

    private function __construct($dbHost, $dbUser, $dbPasswd, $dbName, $dbCharset)
    {

        try {
            $this->dsn = 'mysql:host=' . $dbHost . ';dbname=' . $dbName;
            $this->dbh = new PDO($this->dsn, $dbUser, $dbPasswd);
            $this->dbh->exec('SET character_set_connection=' . $dbCharset . ', character_set_results=' . $dbCharset . ', character_set_client=' . $dbCharset);
        } catch (PDOException $e) {
            $this->outputError($e->getMessage());
        }
    }

    /**
     * 防止克隆
     *
     */
    private function __clone()
    {
    }

    /**
     * Singleton instance
     *
     * @return Object
     */
    public static function getInstance($dbHost = '127.0.0.1', $dbUser = 'hx', $dbPasswd = 'zHedsDp6vwWFJD9m', $dbName = 'hx', $dbCharset = 'utf8mb4')
    {
        if (self::$_instance === null) {
            self::$_instance = new self($dbHost, $dbUser, $dbPasswd, $dbName, $dbCharset);
        }
        return self::$_instance;
    }

    /**
     * Query 查询
     *
     * @param String $strSql SQL语句
     * @param String $queryMode 查询方式(All or Row)
     * @param Boolean $debug
     * @return Array
     */
    public function query($strSql, $queryMode = 'All', $debug = false)
    {
        if ($debug === true) $this->debug($strSql);
        $recordset = $this->dbh->query($strSql);
        $this->getPDOError();
        if ($recordset) {
            $recordset->setFetchMode(PDO::FETCH_ASSOC);
            if ($queryMode == 'All') {
                $result = $recordset->fetchAll();
            } elseif ($queryMode == 'Row') {
                $result = $recordset->fetch();
            }
        } else {
            $result = null;
        }
        return $result;
    }

    /**
     * Update 更新
     *
     * @param String $table 表名
     * @param Array $arrayDataValue 字段与值
     * @param String $where 条件
     * @param Boolean $debug
     * @return Int
     */

    public function update($table, $arrayDataValue, $where = '', $debug = false)
    {
        $this->checkFields($table, $arrayDataValue);
        if ($where) {
            $strSql = '';
            foreach ($arrayDataValue as $key => $value) {
                $strSql .= ", `$key`='$value'";
            }
            $strSql = substr($strSql, 1);
            $strSql = "UPDATE `$table` SET $strSql WHERE $where";
        } else {
            $strSql = "REPLACE INTO `$table` (`" . implode('`,`', array_keys($arrayDataValue)) . "`) VALUES ('" . implode("','", $arrayDataValue) . "')";
        }
        if ($debug === true) $this->debug($strSql);
        $result = $this->dbh->exec($strSql);
        $this->getPDOError();
        return $result;
    }

    /**
     * Insert 插入
     *
     * @param String $table 表名
     * @param Array $arrayDataValue 字段与值
     * @param Boolean $debug
     * @return Int
     */
    public function insert($table, $arrayDataValue, $debug = false)
    {
        $this->checkFields($table, $arrayDataValue);
        $strSql = "INSERT INTO `$table` (`" . implode('`,`', array_keys($arrayDataValue)) . "`) VALUES ('" . implode("','", $arrayDataValue) . "')";
        if ($debug === true) $this->debug($strSql);
        $result = $this->dbh->exec($strSql);
//        $this->getPDOError();
        return $result;
    }

    /**
     * Replace 覆盖方式插入
     *
     * @param String $table 表名
     * @param Array $arrayDataValue 字段与值
     * @param Boolean $debug
     * @return Int
     */
    public function replace($table, $arrayDataValue, $debug = false)
    {
        $this->checkFields($table, $arrayDataValue);
        $strSql = "REPLACE INTO `$table`(`" . implode('`,`', array_keys($arrayDataValue)) . "`) VALUES ('" . implode("','", $arrayDataValue) . "')";
        if ($debug === true) $this->debug($strSql);
        $result = $this->dbh->exec($strSql);
        $this->getPDOError();
        return $result;
    }

    /**
     * Delete 删除
     *
     * @param String $table 表名
     * @param String $where 条件
     * @param Boolean $debug
     * @return Int
     */
    public function delete($table, $where = '', $debug = false)
    {
        if ($where == '') {
            $this->outputError("'WHERE' is Null");
        } else {
            $strSql = "DELETE FROM `$table` WHERE $where";
            if ($debug === true) $this->debug($strSql);
            $result = $this->dbh->exec($strSql);
            $this->getPDOError();
            return $result;
        }
    }

    /**
     * execSql 执行SQL语句
     *
     * @param String $strSql
     * @param Boolean $debug
     * @return Int
     */
    public function execSql($strSql, $debug = false)
    {
        if ($debug === true) $this->debug($strSql);
        $result = $this->dbh->exec($strSql);
        $this->getPDOError();
        return $result;
    }

    /**
     * 获取字段最大值
     *
     * @param string $table 表名
     * @param string $field_name 字段名
     * @param string $where 条件
     */
    public function getMaxValue($table, $field_name, $where = '', $debug = false)
    {
        $strSql = "SELECT MAX(" . $field_name . ") AS MAX_VALUE FROM $table";
        if ($where != '') $strSql .= " WHERE $where";
        if ($debug === true) $this->debug($strSql);
        $arrTemp = $this->query($strSql, 'Row');
        $maxValue = $arrTemp["MAX_VALUE"];
        if ($maxValue == "" || $maxValue == null) {
            $maxValue = 0;
        }
        return $maxValue;
    }

    /**
     * 获取指定列的数量
     *
     * @param string $table
     * @param string $field_name
     * @param string $where
     * @param bool $debug
     * @return int
     */
    public function getCount($table, $field_name, $where = '', $debug = false)
    {
        $strSql = "SELECT COUNT($field_name) AS NUM FROM $table";
        if ($where != '') $strSql .= " WHERE $where";
        if ($debug === true) $this->debug($strSql);
        $arrTemp = $this->query($strSql, 'Row');
        return $arrTemp['NUM'];
    }

    /**
     * 获取表引擎
     *
     * @param String $dbName 库名
     * @param String $tableName 表名
     * @param Boolean $debug
     * @return String
     */
    public function getTableEngine($dbName, $tableName)
    {
        $strSql = "SHOW TABLE STATUS FROM $dbName WHERE Name='" . $tableName . "'";
        $arrayTableInfo = $this->query($strSql);
        $this->getPDOError();
        return $arrayTableInfo[0]['Engine'];
    }

    /**
     * beginTransaction 事务开始
     */
    private function beginTransaction()
    {
        $this->dbh->beginTransaction();
    }

    /**
     * commit 事务提交
     */
    private function commit()
    {
        $this->dbh->commit();
    }

    /**
     * rollback 事务回滚
     */
    private function rollback()
    {
        $this->dbh->rollback();
    }

    /**
     * transaction 通过事务处理多条SQL语句
     * 调用前需通过getTableEngine判断表引擎是否支持事务
     *
     * @param array $arraySql
     * @return Boolean
     */
    public function execTransaction($arraySql)
    {
        $retval = 1;
        $this->beginTransaction();
        foreach ($arraySql as $strSql) {
            if ($this->execSql($strSql) == 0) $retval = 0;
        }
        if ($retval == 0) {
            $this->rollback();
            return false;
        } else {
            $this->commit();
            return true;
        }
    }

    /**
     * checkFields 检查指定字段是否在指定数据表中存在
     *
     * @param String $table
     * @param array $arrayField
     */
    private function checkFields($table, $arrayFields)
    {
        $fields = $this->getFields($table);
        foreach ($arrayFields as $key => $value) {
            if (!in_array($key, $fields)) {
                $this->outputError("Unknown column `$key` in field list.");
            }
        }
    }

    /**
     * getFields 获取指定数据表中的全部字段名
     *
     * @param String $table 表名
     * @return array
     */
    private function getFields($table)
    {
        $fields = array();
        $recordset = $this->dbh->query("SHOW COLUMNS FROM $table");
        $this->getPDOError();
        $recordset->setFetchMode(PDO::FETCH_ASSOC);
        $result = $recordset->fetchAll();
        foreach ($result as $rows) {
            $fields[] = $rows['Field'];
        }
        return $fields;
    }

    /**
     * getPDOError 捕获PDO错误信息
     */
    private function getPDOError()
    {
        if ($this->dbh->errorCode() != '00000') {
            $arrayError = $this->dbh->errorInfo();
            $this->outputError($arrayError[2]);
        }
    }

    /**
     * debug
     *
     * @param mixed $debuginfo
     */
    private function debug($debuginfo)
    {
        var_dump($debuginfo);
        exit();
    }

    /**
     * 输出错误信息
     *
     * @param String $strErrMsg
     */
    private function outputError($strErrMsg)
    {
        throw new Exception('MySQL Error: ' . $strErrMsg);
    }

    /**
     * destruct 关闭数据库连接
     */
    public function destruct()
    {
        $this->dbh = null;
    }
}




//公众号模板消息群发

class WxMessage
{


    /**
     * @return mixed
     */


    //封装数据库连接方法
    public function dataBase()
    {

        $dbms = 'mysql';     //数据库类型
        $host = ''; //数据库主机名
        $dbName = '';    //使用的数据库
        $user = '';      //数据库连接用户名
        $pass = '';          //对应的密码
        $dsn = "$dbms:host=$host;dbname=$dbName";

        try {
            $dbh = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES'utf8';")); //初始化一个PDO对象
//PDO是连接数据库的方法,其内依次放置 ,用户名,密码,展现格式
        } catch (PDOException $e) {
            die ("Error!: " . $e->getMessage() . "<br/>");
        } //处理抛出的异常


        return $dbh;

    }


    var $appid = ''; //开发者appid
    var $appsecret = ''; //开发者密钥

    //access_token需动态获取,存于access_token.php,access_token会过期,过期后需重新获取(清空线上access_token.php里的内容)

    //构造函数,获取token;
    public function __construct($appid = null, $appsecret = null)
    {

        if ($appid && $appsecret) {
            $this->appid = $appid;
            $this->appsecret = $appsecret;
        }

        $this->lasttime = 1406469747;
        $this->access_token = " ";//动态获取access_token

        $file = file_get_contents('access_token.php');
        $file = json_decode($file, true);

        if (!$file || $file['expires_time'] < time()) {

            $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $this->appid . "&secret=" . $this->appsecret . "";
            $res = $this->https_request($url);

            $result = json_decode($res, true);

            $result['expires_time'] = time() + 60;

            file_put_contents('access_token.php', json_encode($result));


        } else {

            $result = $file;
//            exit();

        }

        $this->access_token = $result['access_token'];

//        print_r($this->access_token);

    }


    //发送模板消息
    public function send_template_message($data)
    {

        $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" . $this->access_token;
        $res = $this->https_request($url, $data);

        $result = json_decode($res, true);

        echo "<script> 
                 alert('发送成功!');location.href='http://rs.xinhuang.net.cn/send.php';
              </script>";

    }


    //获取粉丝
    public function getMemberList($openid = '')
    {

        ini_set('max_execution_time', '0');

        $nextOpenid = file_get_contents('next_openid.php');

        if (!$openid && $nextOpenid) {

            $openid = $nextOpenid;

        }



        $url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" . $this->access_token . "&next_openid=" . $openid;
        $res = $this->https_request($url);
        $result = json_decode($res, true);


        if ($result['count'] == 0) {
            return true;
        }

        foreach ($result['data']['openid'] as $key => $value) {


            $isCreate = MyPDO::getInstance()->query('select openid from send where openid= "' . $value . '"', 'Row');

//            print_r($isCreate);

            if ($isCreate) {
                continue;
            }


            $userInfo = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=' . $this->access_token . '&openid=' . $value . '&lang=zh_CN';


            $res = $this->https_request($userInfo);
            $result1 = json_decode($res, true);

            file_put_contents('log.php', file_get_contents('log.php') . '
            ' . $res);


            $openid = $result1['openid'];
            $nickname = '';
            $groupid = $result1['groupid'];


            $sql = "insert into send (openid,nickname,groupid) values('$openid','$nickname','$groupid')";
            MyPDO::getInstance()->insert('send', array('openid' => $openid, 'nickname' => $nickname, 'groupid' => $groupid));


            file_put_contents('next_openid.php', $openid);
            sleep(0.3);

        }


        if ($result['total'] == $result['count']) {
            return true;
        }

        if ($openid != $result['next_openid']) {

            $this->getMemberList($result['next_openid']);

        }


        return true;

//        return $result;

    }


    //https请求(支持GET和POST)
    protected function https_request($url, $data = null)
    {

        $curl = curl_init(); // 初始化一个cURL会话
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $url);
        if (!empty($data)) {
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl); // 抓取 URL 并把它传递给浏览器
        curl_close($curl);
        return $output;

    }

}


//执行操作
if (isset($_POST["submit"])) {

    //添加粉丝进数据库
    if (isset($_POST["sen"])) {

        (new WxMessage())->getMemberList();

    }

    //确认后发送模板消息
    if (isset($_POST["sending"])) {

        $excel = $_POST["excel"];
        date_default_timezone_set('PRC');
        //Excel上传后读取内容
        $PHPExcel = new PHPExcel();

        $filePath = $excel;

        $PHPReader = new PHPExcel_Reader_Excel2007();
        if (!$PHPReader->canRead($filePath)) {
            $PHPReader = new PHPExcel_Reader_Excel5();
            if (!$PHPReader->canRead($filePath)) {
                echo 'no Excel';
                return;
            }
        }

        $PHPExcel = $PHPReader->load($filePath);
        /**读取excel文件中的第一个工作表*/
        $currentSheet = $PHPExcel->getSheet(0);


        /**取得最大的列号*/
        $allColumn = $currentSheet->getHighestColumn();
        /**取得最大的行号*/
        $allRow = $currentSheet->getHighestRow();
        /**从第二行开始输出,因为excel表中第一行为列名*/
        for ($currentRow = 2; $currentRow <= $allRow; $currentRow++) {
            /**从第A列开始输出*/

            $val = $currentSheet->getCellByColumnAndRow(3, $currentRow)->getValue();

            $data = orderSuccess($val);//把从Excel表里面查出来的openid的值作为一个参数赋给$openid
//
            (new WxMessage())->send_template_message(json_encode($data));

        }


//            //测试时使用以下代码,并注释掉上面的发送
//            $data=orderSuccess('');//括号内为用户openid,此处为了不误发信息骚扰用户而使用开发者自己的openid,发送消息成功后,只有开发者本人可以收到信息
//            (new WxMessage())->send_template_message(json_encode($data));


    }

//    exit();

}


//下单成功通知模板
//*
// openid:微信唯一标识
// orderSn:订单号
// goods_name:商品名称
// goods_num:商品数量
// allMoney:总价格
//


function orderSuccess($openid = null, $orderSn = null, $remark = null)
{

    $first = $_POST['first'];
    $activityName = $_POST['activityName'];
    $activityTime = $_POST['activityTime'];
    $remark = $_POST['modelarea'];

    $template = array(
        "touser" => "$openid",
        "template_id" => "8W1ofwzIntnvGoZB3kUX5nCf6aiLyL3XvLV8fKHAOdc",
        'url' => '',//用户点击详情后的跳转地址
        'topcolor' => '#ccc',
        'data' => array('first' => array('value' => $first,
            'color' => "#743A3A",
        ),
            'keyword1' => array('value' => $activityName, //商品名称
                'color' => '#FF0000'
            ),
            'keyword2' => array('value' => "$activityTime", //参加时间
                'color' => '#FF0000'
            ),
            'remark' => array('value' => "$remark", //活动描述
                'color' => '#FF0000'
            ),
        )
    );

//    echo $template;

    return $template;
//    return $remark;
}



?>

<script type="text/javascript" src="/jquery-1.8.0.min.js"></script>
<link rel="stylesheet" href="/layui/css/layui.css">
<script src="/layui/layui.js"></script>


<div style="text-align: center;padding: 20px;font-size: 24px;">微信模板消息发送</div>


<div style="text-align: center;margin-bottom: 20px;">

    <div style="display: inline-block;width: auto;color: white;">
        <form action="" name="send" method="post">
            <input name="sen" type="hidden" value="1"/>
            <input style="padding: 10px 20px;" type="submit" name="submit" value="同步粉丝"
                   onclick="alert('加载需要一点时间,请稍等');return;"/>
        </form>
    </div>

    <div style="height: 15px;"></div>

    <form action="" name="group" method="post">

        <div style="display: inline-block;width: auto;">&nbsp头&nbsp语:
            <input name="first" type="text" value="" placeholder="请输入开头语"
                   style="width:250px;height: 50px;margin-bottom: -20px;text-align: center;"/>
        </div>

        <div style="display: inline-block;width: auto;">
            &nbsp活动名称:
            <input name="activityName" type="text" value="" placeholder="请输入活动名称"
                   style="width:250px;height: 50px;margin-bottom: -20px;text-align: center;"/>
        </div>

        <div style="height: 30px;"></div>

        <div style="display: inline-block;width: auto;">
            参加时间:
            <input name="activityTime" type="text" value="" placeholder="请输入活动时间"
                   style="width:250px;height: 50px;margin-bottom: -20px;text-align: center;"/>
        </div>

        <div style="display: inline-block;width: auto;">
            通知内容:
            <textarea name="modelarea" placeholder="请输入通知内容"
                      style="width:250px;height: 50px;margin-bottom: -20px;"></textarea>
        </div>

        <div style="height: 30px;"></div>

        <div style="display: inline-block;width: auto;color: white;margin: 0 40px;">

            <div>
                <div style="display:inline-block;width:50px;color: black;">Excel:</div>
                <div style="display:inline-block;color: black;">
                    <input type="text" class="form-control form-boxed" name="excel"
                           style="width:250px;height: 40px;text-align: center;">
                    <button type="button" class="btn btn-secondary-outline" id="test1"
                            style="padding:10px;text-align: center;">
                        上传表格
                    </button>
                </div>
            </div>

            <script>
                layui.use(['upload'], function () {

                    var upload = layui.upload;
                    var uploadInst = upload.render({
                        elem: '#test1' //绑定元素
                        , url: 'upload.php?ProType=uploadImg' //上传接口
                        , accept: 'file'
                        , done: function (res) {

                            console.log('yes')

                            //上传完毕回调
                            var res = res.data;
                            $('[name=excel]').val(res.path);
                        }
                        , error: function () {
                            //请求异常回调

                            console.log('no')

                        }
                    });
                });
            </script>

        </div>

        <div style="display: inline-block;width: auto;margin: 0 60px;">
            <input name="sending" type="hidden" value=" "/>
            <input id="submit" style="padding: 10px 20px;" type="submit" name="submit" value="发送消息"/>
        </div>

    </form>

</div>


<div style="text-align: center;font-size: 24px;">用户信息(由于用户过多,列表仅显示最后50条)</div>

<table class="table table-bordered table-striped table-hover" style="width: 90%;border: 1px solid black;margin: 0 auto;margin-top: 10px;">
    <thead style="border: 1px solid black;">
    <tr style="border: 1px solid black;">
        <th style="border: 1px solid black;">序号</th>
        <th style="border: 1px solid black;">用户openid</th>
        <th style="border: 1px solid black;">用户分组</th>
    </tr>
    </thead>

    <tbody>

    <?php

    $sql_r = "SELECT * FROM send ORDER BY id DESC limit 50";

    foreach ((new WxMessage())->dataBase()->query($sql_r) as $key => $values) {
        ?>
        <tr class="cen">
            <td style="border: 1px solid black;color: #b2b2b2;text-align: center;margin: 0;"><?php echo $values['id']; ?></td>
            <td style="border: 1px solid black;color: #b2b2b2;text-align: center;margin: 0;"><?php echo $values['openid']; ?></td>
            <td style="border: 1px solid black;color: #b2b2b2;text-align: center;margin: 0;"><?php echo $values['groupid']; ?></td>
        </tr>
        <?php

    }
    ?>

    </tbody>

</table>

 








upload.php


<?php

//本文件用于控制upload文件夹
define('__UPLOAD__','upload');

class Upload
{

    protected $tmpName;
    protected $name;
    protected $size;
    protected $type;
    protected $file_type;
    protected $uploadDir;
    public $dir =__UPLOAD__;

    public function __construct()
    {
        $this->uploadDir = __UPLOAD__;
        $file= $_FILES['file'];
        $this->name = $file['name'];
        $this->tmpName = $file['tmp_name'];
        $this->size = $file['size'];
        $this->type = $file['type'];
        if (!is_dir($this->uploadDir)) {
            mkdir($this->uploadDir);
            chmod($this->uploadDir, 0777);
        }
        $this->typeDir();



    }


    public function typeDir ()
    {

        switch ($this->type) {

            case 'image/jpeg' :
            case 'image/png' :
            case 'image/bmp' :
                $this->file_type = 'img';
                $this->makeDir('img');

                break;
            case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
            case 'application/vnd.ms-excel':
            case 'application/msword':
            case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
                $this->file_type = 'office';
                $this->makeDir('office');

                break;
            default:
        }


    }

    public function makeDir($type)
    {

        $images_dir = $this->uploadDir . '/images';
        $office_dir = $this->uploadDir . '/office';

        if ($type=='img') {

            if (!is_dir($images_dir)) {

                mkdir($images_dir);
                chmod($images_dir, 0777);

            }

            $this->dir = $images_dir;
        }

        if ($type=='office') {

            if (!is_dir($office_dir)) {

                mkdir($office_dir);
                chmod($office_dir, 0777);

            }

            $this->dir = $office_dir;
        }

    }

    public function move ()
    {
        $path = pathinfo($this->name);
        $name =md5($path['filename'].time()) . '.' . $path['extension'];
        $path_name = $this->dir . '/' . $name;
        $result= move_uploaded_file($this->tmpName,$path_name);

        if ($result) {
            return array('path'=>trim($path_name,'.'),'type'=>$this->file_type,'name'=>$this->name,'id'=>md5(substr(microtime(), 2, 5).rand(0,99)));
        }
        return false;

    }


}
$result= new Upload();
$result= $result->move();
if ($result) {

    print_r(json_encode(array('code'=>200,'data'=>$result)));
}

//print_r($result);

 




以下为最终效果:

 

 





















posted @ 2019-07-18 15:29  远方的异特  阅读(1393)  评论(0编辑  收藏  举报