<?php

/**
 * tpshop
 * ============================================================================
 * 版权所有 2017-2027 深圳搜豹网络科技有限公司，并保留所有权利。
 * 网站地址: http://www.tp-shop.cn
 * ----------------------------------------------------------------------------
 * 这不是一个自由软件！您只能在不用于商业目的的前提下对程序代码进行修改和使用 .
 * 不允许对程序代码以任何形式任何目的的再发布。
 * 如果商业用途务必到官方购买正版授权, 以免引起不必要的法律纠纷.
 * ============================================================================
 * author: lhb
 * Date: 2017-5-8
 */

namespace app\common\logic\wechat;

use think\Db;
use think\Cache;

/**
 * 说明：此类只进行微信公众号的接口封装，不实现业务逻辑！
 * 接口统一错误返回false，错误信息由getError()获取
 * 业务逻辑请前往 WechatLogic
 */
class WechatUtil extends WxCommon
{
    private $config = [];    //微信公众号配置
    private $tagsMap = null; //粉丝标签映射
    private $events = [];

    //事件类型
    const EVENT_ALL = 0; //有事件就处理
    const EVENT_TEXT = 1; //文本输入事件
    const EVENT_SUBSCRIBE = 2; //关注事件
    const EVENT_UNSUBSCRIBE = 3; //取消关注事件
    const EVENT_SCAN = 4; //已关注的扫描二维码事件
    const EVENT_LOCATION = 5; //上报二维码时间
    const EVENT_CLICK = 6; //点击菜单事件
    const EVENT_VIEW = 7; //点击菜单跳转链接事件


    public function __construct($config = null)
    {
        if ($config === null) {
            $config = Db::name('wx_user')->find();
        }
        $this->config = $config;
    }

    /**
     * 获取access_token
     * @return string
     */
    public function getAccessToken()
    {
        $wechat = $this->config;
        if (empty($wechat)) {
            $this->setError("公众号不存在！");
            return false;
        }

        //判断是否过了缓存期
        $expire_time = $wechat['web_expires'];
        if ($expire_time > time()) {
            return $wechat['web_access_token'];
        }

        $appid = $wechat['appid'];
        $appsecret = $wechat['appsecret'];
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$appsecret}";
        $return = $this->requestAndCheck($url, 'GET');
        if (!isset($return['access_token'])) {
            $this->config['web_expires'] = 0;
            Db::name('wx_user')->where('id', $wechat['id'])->save(['web_expires' => 0]);
            return false;
        }

        $web_expires = time() + 7000; // 提前200秒过期
        Db::name('wx_user')->where('id', $wechat['id'])->save(['web_access_token'=>$return['access_token'], 'web_expires'=>$web_expires]);
        $this->config['web_access_token'] = $return['access_token'];
        $this->config['web_expires'] = $web_expires;

        return $return['access_token'];
    }

    /**
     * 获取粉丝详细信息
     * @param string $openid
     * @param string $access_token 如果为null，自动获取
     * @return array|bool
     */
    public function getFanInfo($openid, $access_token = null)
    {
        if (null === $access_token) {
            if (!$access_token = $this->getAccessToken()) {
                return false;
            }
        }

        $url ="https://api.weixin.qq.com/cgi-bin/user/info?access_token={$access_token}&openid={$openid}&lang=zh_CN";
        $return = $this->requestAndCheck($url, 'GET');
        if ($return === false) {
            return false;
        }

        /* $wxdata[]元素：
         * subscribe	用户是否订阅该公众号标识，值为0时，代表此用户没有关注该公众号，拉取不到其余信息。
         * openid	用户的标识，对当前公众号唯一
         * nickname	用户的昵称
         * sex	用户的性别，值为1时是男性，值为2时是女性，值为0时是未知
         * city	用户所在城市
         * country	用户所在国家
         * province	用户所在省份
         * language	用户的语言，简体中文为zh_CN
         * headimgurl	用户头像，最后一个数值代表正方形头像大小（有0、46、64、96、132数值可选，0代表640*640正方形头像），用户没有头像时该项为空。若用户更换头像，原有头像URL将失效。
         * subscribe_time	用户关注时间，为时间戳。如果用户曾多次关注，则取最后关注时间
         * unionid	只有在用户将公众号绑定到微信开放平台帐号后，才会出现该字段。
         * remark	公众号运营者对粉丝的备注，公众号运营者可在微信公众平台用户管理界面对粉丝添加备注
         * groupid	用户所在的分组ID（兼容旧的用户分组接口）
         * tagid_list	用户被打上的标签ID列表
         */
        $return['sex_name'] = $this->sexName($return['sex']);
        return $return;
    }

    /**
     * sex_id 用户的性别，值为1时是男性，值为2时是女性，值为0时是未知
     */
    public function sexName($sex_id)
    {
        if ($sex_id == 1) {
            return '男';
        } else if ($sex_id == 2) {
            return '女';
        }
        return '未知';
    }

    /**
     * 获取粉丝标签
     * @return mixed
     */
    public function getAllFanTags()
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $url = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'GET');
        if ($return === false) {
            return false;
        }

        //$wxdata数据样例：{"tags":[{"id":1,"name":"每天一罐可乐星人","count":0/*此标签下粉丝数*/}, ...]}
        return $return['tags'];
    }

    /**
     * 获取所有用户标签
     * @return array|bool
     */
    public function getAllFanTagsMap()
    {
        if ($this->tagsMap !== null) {
            return $this->tagsMap;
        }

        $user_tags = $this->getAllFanTags();
        if ($user_tags === false) {
            return false;
        }

        $this->tagsMap = [];
        foreach ($user_tags as $tag) {
            $this->tagsMap[$tag['id']] = $this->tagsMap[$tag['name']];
        }
        return $this->tagsMap;
    }

    /**
     * 获取粉丝标签名
     * @param array $tagid_list
     * @param array $tagsMap
     * @return array|bool
     */
    public function getFanTagNames($tagid_list)
    {
        if ($this->tagsMap === null) {
            $tagsMap = $this->getAllFanTagsMap();
            if ($tagsMap === false) {
                return false;
            }
            $this->tagsMap = $tagsMap;
        }

        $tag_names = [];
        foreach ($tagid_list as $tag) {
            $tag_names[] = $this->tagsMap[$tag];
        }
        return $tag_names;
    }

    /**
     * 获取粉丝id列表
     * @param string $next_openid 下一次拉取的起始id的前一个id
     * @return array|bool
     */
    public function getFanIdList($next_openid='')
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $url ="https://api.weixin.qq.com/cgi-bin/user/get?access_token={$access_token}&next_openid={$next_openid}";//重头开始拉取，一次最多拉取10000个
        $return = $this->requestAndCheck($url, 'GET');
        if ($return === false) {
            return false;
        }

        //$list[]元素：
        //total	关注该公众账号的总用户数
        //count	拉取的OPENID个数，最大值为10000
        //data	列表数据，OPENID的列表
        //next_openid	拉取列表的最后一个用户的OPENID
        //样本数据：{"total":2,"count":2,"data":{"openid":["OPENID1","OPENID2"]},"next_openid":"NEXT_OPENID"}
        return $return;
    }

    /**
     * 设置粉丝备注
     */
    public function setFanRemark($openid, $remark)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson(['openid '=> $openid, 'remark' => $remark]);
        $url ="https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }

        return true;
    }

    /*
     * 向一个粉丝发送消息
     * 文档：https://mp.weixin.qq.com/wiki?action=doc&id=mp1421140547#2
     * @param $type string (text,news,image,voice,video,music,mpnews,wxcard)
     */
    public function sendMsgToOne($openid, $type, $content)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $data = [
            'touser' => $openid,
            'msgtype' => $type,
        ];

        if ($type == 'text') {
            $data[$type]['content'] = $content; //text
        } elseif (in_array($type, ['image', 'voice', 'mpnews'])) {
            $data[$type]['media_id'] = $content; //media_id
        } elseif ($type == 'wxcard') {
            $data[$type]['card_id'] = $content; //card_id
        } elseif ($type == 'news') {
            //$content = [{
            //     "title":"Happy Day",
            //     "description":"Is Really A Happy Day",
            //     "url":"URL",
            //     "picurl":"PIC_URL"
            //}, ...]
            $data[$type]['articles'] = $content;
        } elseif ($type == 'video') {
            //$content = {
            //    "media_id":"MEDIA_ID",
            //    "thumb_media_id":"MEDIA_ID",
            //    "title":"TITLE",
            //    "description":"DESCRIPTION"
            //}
            $data[$type] = $content;
        } elseif ($type == 'music') {
            //$content = {
            //    "title":"MUSIC_TITLE",
            //    "description":"MUSIC_DESCRIPTION",
            //    "musicurl":"MUSIC_URL",
            //    "hqmusicurl":"HQ_MUSIC_URL",
            //    "thumb_media_id":"THUMB_MEDIA_ID"
            //}
            $data[$type] = $content;
        }

        $post = $this->toJson($data);
        $url ="https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }

        return true;
    }

    /**
     * 指定一部分人群发消息，只有服务号可用
     * @param array|string $openids
     * @param $type string (text,image,voice,mpvideo,mpnews,wxcard)
     * @return boolean|array
     */
    public function sendMsgToMass($openids, $type, $content)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        if (is_string($openids)) {
            $openids = explode(',', $openids);
        }
        $data = [
            'touser' => $openids,
            'msgtype' => $type,
        ];

        if ($type == 'text') {
            $data[$type]['content'] = $content; //text
        } elseif (in_array($type, ['image', 'voice'])) {
            $data[$type]['media_id'] = $content; //media_id
        } elseif ($type == 'mpnews') {
            $data[$type]['media_id'] = $content; //media_id
            $data[$type]['send_ignore_reprint'] = 1;//图文消息被判定为转载时，是否继续群发。 1为继续群发（转载），0为停止群发
        } elseif ($type == 'wxcard') {
            $data[$type]['card_id'] = $content; //card_id
        } elseif ($type == 'mpvideo') {
            //$content = {
            //    "media_id":"MEDIA_ID",
            //    "title":"TITLE",
            //    "thumb_media_id":"MEDIA_ID",
            //    "description":"DESCRIPTION"
            //}
            $data[$type] = $content;
        }

        $post = $this->toJson($data);
        $url ="https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }

        return [
            'type' => $return['type'],//媒体文件类型，分别有图片（image）、语音（voice）、视频（video）和缩略图（thumb），次数为news，即图文消息
            'msg_id' => $return['type'], //消息发送任务的ID
            'msg_data_id' => $return['msg_data_id'],//消息的数据ID,可以用于在图文分析数据接口中，获取到对应的图文消息的数据
        ];
    }

    /**
     * 给同一标签的所有粉丝发消息
     * @param int $tag_id 群发到的标签的tag_id, 0则表示发送给所有粉丝
     * @param $type string (text,image,voice,mpvideo,mpnews,wxcard)
     * @param mixed $content
     * @return boolean|array
     */
    public function sendMsgToAll($tag_id, $type, $content)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $data = [
            'filter' => ['is_to_all' => !boolval($tag_id), 'tag_id' => $tag_id],
            'msgtype' => $type,
        ];

        if ($type == 'text') {
            $data[$type]['content'] = $content; //text
        } elseif (in_array($type, ['image', 'voice'])) {
            $data[$type]['media_id'] = $content; //media_id
        } elseif ($type == 'mpnews') {
            $data[$type]['media_id'] = $content; //media_id
            $data[$type]['send_ignore_reprint'] = 1;//图文消息被判定为转载时，是否继续群发。 1为继续群发（转载），0为停止群发
        } elseif ($type == 'wxcard') {
            $data[$type]['card_id'] = $content; //card_id
        } elseif ($type == 'mpvideo') {
            //$content = {
            //    "media_id":"MEDIA_ID",
            //    "title":"TITLE",
            //    "thumb_media_id":"MEDIA_ID",
            //    "description":"DESCRIPTION"
            //}
            $data[$type] = $content;
        }

        $post = $this->toJson($data);
        $url ="https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }

        return [
            'type' => $return['type'],//媒体文件类型，分别有图片（image）、语音（voice）、视频（video）和缩略图（thumb），次数为news，即图文消息
            'msg_id' => $return['type'], //消息发送任务的ID
            'msg_data_id' => $return['msg_data_id'],//消息的数据ID,可以用于在图文分析数据接口中，获取到对应的图文消息的数据
        ];
    }

    /**
     * 发送消息，自动识别id数
     * @param string|array $openids
     * @param $type string (text,image,voice,mpvideo,mpnews,wxcard)
     * @param mixed $content
     * @return boolean
     */
    public function sendMsg($openids, $type, $content)
    {
        if (empty($openids)) {
            return true;
        }
        if (is_string($openids)) {
            $openids = explode(',', $openids);
        }

        if (count($openids) > 1) {
            $result = $this->sendMsgToMass($openids, $type, $content);
        } else {
            $result = $this->sendMsgToOne($openids[0], $type, $content);
        }
        if ($result === false) {
            return false;
        }

        return true;
    }

    /**
     * 新增媒质永久素材
     * 文档：https://mp.weixin.qq.com/wiki?action=doc&id=mp1444738729
     * @parem type $path 素材地址
     * @param string $type 类型有image,voice,video,thumb
     * @param array $param 目前是video类型需要
     * @return {"media_id":MEDIA_ID,"url":URL}
     */
    public function uploadMaterial($path, $type, $param=[])
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post_arr = ['media' => '@'.$path];
        if ($type == 'video') {
            $post_arr['description'] = $this->toJson([
                'title' => $param['title'],
                'introduction' => $param['introduction'],
            ]);
        }

        $url ="https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={$access_token}&type={$type}";
        $return = $this->requestAndCheck($url, 'POST', $post_arr);
        if ($return === false) {
            return false;
        }

        return $return;
    }

    /**
     * 上传图文素材。 说明：news里面的图片只能用news_image，封面用image
     * 文档：https://mp.weixin.qq.com/wiki?action=doc&id=mp1444738729
     * @param array $articles
     *  [
     *      [
     *          "title"=> TITLE,
     *          "thumb_media_id"=> THUMB_MEDIA_ID, //封面图片素材id
     *          "author"=> AUTHOR,
     *          "digest"=> DIGEST, //图文消息的摘要，仅有单图文消息才有摘要，多图文此处为空。如果本字段为没有填写，则默认抓取正文前64个字。
     *          "show_cover_pic"=> SHOW_COVER_PIC(0 / 1), //是否显示封面，0为false，即不显示，1为true，即显示
     *          "content"=> CONTENT, //图文消息的具体内容，支持HTML标签，必须少于2万字符，小于1M，且此处会去除JS,
     *                                  涉及图片url必须来源"上传图文消息内的图片获取URL"接口获取。外部图片url将被过滤。
     *          "content_source_url"=> CONTENT_SOURCE_URL //图文消息的原文地址，即点击“阅读原文”后的URL
     *      ],
     *      //若新增的是多图文素材，则此处应还有几段articles结构(最多8段)
     *  ]
     * @return string|bool MEDIA_ID
     */
    public function uploadNews($articles)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson(["articles" => $articles]);
        $url ="https://api.weixin.qq.com/cgi-bin/material/add_news?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }

        return $return['media_id'];
    }

    /**
     * 上传图文消息中的图片
     * 文档：https://mp.weixin.qq.com/wiki?action=doc&id=mp1444738729
     * @param string $path 图片地址
     * @return string|bool 图片的url
     */
    public function uploadNewsImage($path)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post_arr = ["media"=>'@'.$path];
        $url ="https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post_arr);
        if ($return === false) {
            return false;
        }

        return $return['url'];
    }

    /**
     * 上传临时材料（3天内有效）
     * 文档：https://mp.weixin.qq.com/wiki?action=doc&id=mp1444738726
     * @parem type $path 素材地址
     * @param string $type 类型有image,voice,video,thumb
     * @return array|bool {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789}
     */
    public function uploadTempMaterial($path, $type = 'image')
    {
        if (!($access_token = $this->getAccessToken())) {
            return false;
        }

        $post_arr = ['media' => '@'.$path];
        $url ="https://api.weixin.qq.com/cgi-bin/media/upload?access_token={$access_token}&type={$type}";
        $return = $this->requestAndCheck($url, 'POST', $post_arr);
        if ($return === false) {
            return false;
        }

        return $return;
    }

    /**
     * 更新一篇图文
     * 文档：https://mp.weixin.qq.com/wiki?action=doc&id=mp1444738732&t=0.5904919423628598
     * @param string $mediaId MEDIA_ID
     * @param array $article INDEX
    {
    "title": TITLE,
    "thumb_media_id": THUMB_MEDIA_ID,
    "author": AUTHOR,
    "digest": DIGEST,
    "show_cover_pic": SHOW_COVER_PIC(0 / 1),
    "content": CONTENT,
    "content_source_url": CONTENT_SOURCE_URL
    }
     * @param number $index 要更新的文章在图文消息中的位置（多图文消息时，此字段才有意义），第一篇为0
     * @return boolean
     */
    public function updateNews($mediaId, $article, $index = 0)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson([
            'media_id' => $mediaId,
            'index' => $index,
            'articles' => $article
        ]);

        $url ="https://api.weixin.qq.com/cgi-bin/material/update_news?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }

        return true;
    }

    /**
     * 获取图文素材
     * @param string $mediaId
     * @return boolean|array
     */
    public function getNews($mediaId)
    {
        $wxdata = $this->getMaterial($mediaId);
        if ($wxdata === false) {
            return false;
        }

//    [
//        [
//        title 图文消息的标题
//        thumb_media_id	图文消息的封面图片素材id（必须是永久mediaID）
//        show_cover_pic	是否显示封面，0为false，即不显示，1为true，即显示
//        author	作者
//        digest	图文消息的摘要，仅有单图文消息才有摘要，多图文此处为空
//        content	图文消息的具体内容，支持HTML标签，必须少于2万字符，小于1M，且此处会去除JS
//        url	图文页的URL
//        content_source_url	图文消息的原文地址，即点击“阅读原文”后的URL
//        ],
//        //多图文消息有多篇文章
//     ]
        return $wxdata['news_item'];
    }

    /**
     * 获取媒质素材
     * @param string $mediaId
     * @return boolean
    array video返回{
    "title":TITLE,
    "description":DESCRIPTION,
    "down_url":DOWN_URL,
    }
     */
    public function getMaterial($mediaId)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson(['media_id' => $mediaId]);
        $url ="https://api.weixin.qq.com/cgi-bin/material/get_material?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }
        return true;
    }

    /**
     * 删除素材，包括图文
     * @param string $mediaId
     * @return boolean
     */
    public function delMaterial($mediaId)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson(['media_id' => $mediaId]);
        $url ="https://api.weixin.qq.com/cgi-bin/material/del_material?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }

        return true;
    }

    /**
     * 获取素材总数
     * @return array|bool
    //voice_count	语音总数量
    //video_count	视频总数量
    //image_count	图片总数量
    //news_count	图文总数量
     */
    public function getMaterialCount()
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $url ="https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'GET');
        if ($return === false) {
            return false;
        }

        return $return;
    }

    /**
     * 获取素材列表
     * @param string $type 素材的类型，图片（image）、视频（video）、语音 （voice）、图文（news）
     * @param int $offset 从全部素材的该偏移位置开始返回，0表示从第一个素材 返回
     * @param int $count 返回素材的数量，取值在1到20之间
     * @return array|bool
     */
    public function getMaterialList($type, $offset, $count)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson([
            'type' => $type,
            'offset' => $offset,
            'count' => $count
        ]);

        $url ="https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }

        /* 返回图文消息结构 */
        //{
        //  "total_count": TOTAL_COUNT,
        //  "item_count": ITEM_COUNT,
        //  "item": [{
        //      "media_id": MEDIA_ID,
        //      "content": {
        //          "news_item": [{
        //              "title": TITLE,
        //              "thumb_media_id": THUMB_MEDIA_ID,
        //              "show_cover_pic": SHOW_COVER_PIC(0 / 1),
        //              "author": AUTHOR,
        //              "digest": DIGEST,
        //              "content": CONTENT,
        //              "url": URL,
        //              "content_source_url": CONTETN_SOURCE_URL
        //          },
        //          //多图文消息会在此处有多篇文章
        //          ]
        //       },
        //       "update_time": UPDATE_TIME
        //   },
        //   //可能有多个图文消息item结构
        // ]
        //}

        /*其他类型*/
        //{
        //  "total_count": TOTAL_COUNT,
        //  "item_count": ITEM_COUNT,
        //  "item": [{
        //      "media_id": MEDIA_ID,
        //      "name": NAME,
        //      "update_time": UPDATE_TIME,
        //      "url":URL
        //  },
        //  //可能会有多个素材
        //  ]
        //}
        return $return;
    }

    /**
     * 创建临时二维码
     * @param int $expire 过期时间，单位秒，最大30天，即2592000秒
     * @param int $scene_id 场景id，用户自定义，目前支持1-100000
     * @return boolean
     */
    public function createTempQrcode($expire, $scene_id)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson([
            'expire_seconds' => $expire,
            'action_name'    => 'QR_SCENE',
            'action_info'    => [
                'scene' => [
                    'scene_id' => $scene_id
                ]
            ]
        ]);

        $url ="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }

//        返回数据格式：
//        {
//            "ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==",
//            "expire_seconds":60,
//            "url":"http:\/\/weixin.qq.com\/q\/kZgfwMTm72WWPkovabbI"
//        }

        return $return;
    }

    /**
     * 获取用户所有模板消息
     * @return bool|mixed|string
     */
    public function getAllTemplateMsg()
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $url ="https://api.weixin.qq.com/cgi-bin/material/get_all_private_template?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'GET');
        if ($return === false) {
            return false;
        }

        //返回数据格式：
        //{"template_list": [{
        //    "template_id": "iPk5sOIt5X_flOVKn5GrTFpncEYTojx6ddbt8WYoV5s",
        //    "title": "领取奖金提醒",
        //    "primary_industry": "IT科技",
        //    "deputy_industry": "互联网|电子商务",
        //    "content": "{ {result.DATA} }\n\n领奖金额:{ {withdrawMoney.DATA} }\n领奖  时间:{ {withdrawTime.DATA} }\n银行信息:{ {cardInfo.DATA} }\n到账时间:  { {arrivedTime.DATA} }\n{ {remark.DATA} }",
        //    "example": "您已提交领奖申请\n\n领奖金额：xxxx元\n领奖时间：2013-10-10 12:22:22\n银行信息：xx银行(尾号xxxx)\n到账时间：预计xxxxxxx\n\n预计将于xxxx到达您的银行卡"
        //}, ...]}
        return $return;
    }

    /**
     * 添加消息模板
     * @param $template_sn string 模板编号
     * @return bool|string 模板id
     */
    public function addTemplateMsg($template_sn)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson(['template_id_short' => $template_sn]);
        $url ="https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }
        return $return['template_id'];
    }

    /**
     * 删除模板消息
     * @param $template_id string 模板id
     * @return bool
     */
    public function delTemplateMsg($template_id)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson(['template_id' => $template_id]);
        $url ="https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }
        return true;
    }

    public function sendTemplateMsg($openid, $template_id, $url, $data)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson([
            "touser" => $openid,
            "template_id" => $template_id,
            "url" => $url, //模板跳转链接
//            "miniprogram" => [ //小程序跳转配置
//                "appid" => "xiaochengxuappid12345",
//                "pagepath" => "index?foo=bar"
//            ],
            "data" => $data, //模板数据
//            [
//                "first" =>  [
//                    "value" => "恭喜你购买成功！",
//                    "color" => "#173177"
//                ],
//                "keynote1" => [
//                    "value" => "巧克力",
//                    "color" => "#173177"
//                ],
//                "remark" => [
//                    "value" => "欢迎再次购买！",
//                    "color" => "#173177"
//                ]
//            ]
        ]);
        //注：url和miniprogram都是非必填字段，若都不传则模板无跳转；若都传，会优先跳转至小程序。
        //开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时，将会跳转至url

        $url ="https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }
        return true;
    }

    /**
     * 创建自定义菜单
     * 文档：https://mp.weixin.qq.com/wiki?action=doc&id=mp1421141013
     * @param $data array
     * @return bool
     */
    public function createMenu($data)
    {
        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $post = $this->toJson($data);
        $url ="https://api.weixin.qq.com/cgi-bin/menu/create?access_token={$access_token}";
        $return = $this->requestAndCheck($url, 'POST', $post);
        if ($return === false) {
            return false;
        }
        return true;
    }

    /**
     * 获取ticket。 jsapi_ticket是公众号用于调用微信JS接口的临时票据
     * 文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
     * @param string $type ticket类型（jsapi,wx_card）
     * @return bool
     */
    public function getTicket($type = 'jsapi')
    {
        $key = 'weixin_ticket_'.$type;
        $ticket = Cache::get($key);
        if (!empty($ticket)) {
            return $ticket;
        }

        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$access_token}&type={$type}";
        $return = $this->requestAndCheck($url, 'GET');
        if ($return === false) {
            return false;
        }

        Cache::set($key, $return['ticket'], 7000);
        return $return['ticket'];
    }

    /**
     * 签名
     * @param string $url
     * @return array|bool
     */
    public function getSignPackage($url = '')
    {
        $ticket = $this->getTicket();
        if ($ticket === false) {
            return false;
        }

        // 注意 URL 一定要动态获取，不能 hardcode.
        $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
        $url = $url ?: $protocol.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        $timestamp = time();
        $nonceStr = $this->createNonceStr();

        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket=$ticket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
        $signature = sha1($string);

        $signPackage = [
            "appId" => $this->config['appid'],
            "nonceStr" => $nonceStr,
            "timestamp" => $timestamp,
            "url" => $url,
            "rawString" => $string,
            "signature" => $signature
        ];
        return $signPackage;
    }

    /**
     * 随机字符串
     * @param int $length
     * @return string
     */
    private function createNonceStr($length = 16)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 推送消息处理接口
     * @return array|bool
     */
    public function getPushMessage()
    {
        $content = $GLOBALS['HTTP_RAW_POST_DATA'] ?: file_get_contents('php://input');

        $this->logDebugFile($content);

        $message = \app\common\util\XML::parse($content);
        if (empty($message)) {
            $this->setError('推送消息为空！');
            return false;
        }

        $this->logDebugFile($message);

        return $message;
    }

    /**
     * 订阅消息事件
     * @param $event_type
     * @param $callback
     */
    public function registerMsgEvent($event_type, $callback)
    {
        $this->events[$event_type] = $callback;
    }

    /**
     * 处理消息事件
     */
    public function handleMsgEvent()
    {
        $msg = $this->getPushMessage();
        if (!$msg) {
            exit($this->getError());
        }

        // 先处理全局事件
        if (isset($this->events[self::EVENT_ALL]) && is_callable($this->events[self::EVENT_ALL])) {
            $this->events[self::EVENT_ALL]($msg);
        }

        static $event_parse = [
            self::EVENT_TEXT        => ['MsgType' => 'text'],
            self::EVENT_SUBSCRIBE   => ['MsgType' => 'event', 'Event' => 'subscribe'],
            self::EVENT_UNSUBSCRIBE => ['MsgType' => 'event', 'Event' => 'unsubscribe'],
            self::EVENT_SCAN        => ['MsgType' => 'event', 'Event' => 'SCAN'],
            self::EVENT_LOCATION    => ['MsgType' => 'event', 'Event' => 'LOCATION'],
            self::EVENT_CLICK       => ['MsgType' => 'event', 'Event' => 'CLICK'],
            self::EVENT_VIEW        => ['MsgType' => 'event', 'Event' => 'VIEW'],
        ];

        // 找出注册的事件并处理
        foreach ($this->events as $event => $callback) {
            if ( ! isset($event_parse[$event])) {
                continue;
            }

            $find_event = true;
            foreach ($event_parse[$event] as $key => $word) {
                if ($msg[$key] !== $word) {
                    $find_event = false;
                    break;
                }
            }
            if ( ! $find_event) {
                continue;
            }

            is_callable($callback) && $callback($msg);
            break;
        }
        
        if($msg=='1' || $msg[$key] == 'LOCATION') return;
        $return= $this->createReplyMsgOfText($msg['ToUserName'], $msg['FromUserName'], '你已关注公众号');
        exit($return);
    }

    /**
     * 创建文本回复消息
     * @param string $fromUser
     * @param string $toUser
     * @param string $text
     * @return string
     */
    public function createReplyMsgOfText($fromUser, $toUser, $text)
    {
        $time = time();
        $template =
            "<xml>
            <ToUserName><![CDATA[$toUser]]></ToUserName>
            <FromUserName><![CDATA[$fromUser]]></FromUserName>
            <CreateTime>$time</CreateTime>
            <MsgType><![CDATA[text]]></MsgType>
            <Content><![CDATA[$text]]></Content>
            </xml>";
        return $template;
    }

    /**
     * 创建图片回复消息
     * @param string $fromUser
     * @param string $toUser
     * @param string $mediaId
     * @return string
     */
    public function createReplyMsgOfImage($fromUser, $toUser, $mediaId)
    {
        $time = time();
        $template =
            "<xml>
            <ToUserName><![CDATA[$toUser]]></ToUserName>
            <FromUserName><![CDATA[$fromUser]]></FromUserName>
            <CreateTime>$time</CreateTime>
            <MsgType><![CDATA[image]]></MsgType>
            <Image>
            <MediaId><![CDATA[$mediaId]]></MediaId>
            </Image>
            </xml>";
        return $template;
    }

    /**
     * 创建图文回复消息
     * @param string $fromUser
     * @param string $toUser
     * @param array $articles
     * @return string
     */
    public function createReplyMsgOfNews($fromUser, $toUser, $articles)
    {
        $articles = array_slice($articles, 0, 7);//最多支持7个
        $num = count($articles);
        if (!$num) {
            return '';
        }

        $itemTpl = '';
        foreach ($articles as $item) {
            $itemTpl .=
            "<item>
            <Title><![CDATA[{$item['title']}]]></Title> 
            <Description><![CDATA[{$item['description']}]]></Description>
            <PicUrl><![CDATA[{$item['picurl']}]]></PicUrl>
            <Url><![CDATA[{$item['url']}]]></Url>
            </item>";
        }

        $time = time();
        $template =
            "<xml>
            <ToUserName><![CDATA[$toUser]]></ToUserName>
            <FromUserName><![CDATA[$fromUser]]></FromUserName>
            <CreateTime>$time</CreateTime>
            <MsgType><![CDATA[news]]></MsgType>
            <ArticleCount>$num</ArticleCount>
            <Articles>$itemTpl</Articles>
            </xml>";
        return $template;
    }
}