半个轮子:微信模板消息推送

随着微信小程序、手机APP项目的愈发普遍。单一的轮子会造成项目愈发臃肿。很多时候我们为了图方便都是直接引入别人的类库,很少关心自己是否实现一个类库。

今天波波分享的半个轮子属于一个半成品。其想法是将微信模板消息推送、阿里云移动推送、个推(集成这个主要原因是uni-app中移动端有此插件,集成后可以让前端技术更方便)、邮件、短信这几个功能放到一个PHP文件中。使用时只需要一句话进行调用。

调用代码片段:

  1. /**
  2.      * 工单提醒
  3.      */
  4.     public function reminder(){
  5.         $msg = ["touser"=>1,"method"=>"wechat","scene"=>"remind","msg_type"=>"text",
  6.             "content"=>["first"=>["value"=>"您有一条工单即将到期","color"=>"#173177"],
  7.                 "keyword1"=>["value"=>"GD2018080133420","color"=>"#173177"],"keyword2"=>["value"=>"保养任务","color"=>"#173177"],
  8.                 "keyword3"=>["value"=>"2021-03-30 12:30:00","color"=>"#173177"],"remark"=>['value'=>"这是一条测试提醒"]],'title'=>"工单提醒",'openid'=>"oytM-6Y_oTomRkXPjxHJsIdMhVP0"];
  9.         $res = NotifyService::send($msg);
  10.         $code = $res['code'] || 200;
  11.         $this->result([],$code,$res['msg'],'json');
  12.     }

关键在于使用时仅需要在调用的PHP文件上方引入NotifyService,直接使用send()方法即可。

NotifyService源码(半成品):

  1. <?php
  2. namespace service;
  3. /**
  4.  * 消息推送服务
  5.  * Class NotifyService.php
  6.  * @package app\admin\controller
  7.  * @author 繁华如梦 <[email protected]>
  8.  * @desc:基于阿里移动推送、邮件、个推、微信公众号做消息推送
  9.  * @date 2021/3/8 9:33
  10.  */
  11. class NotifyService{
  12.     private static $content = [];
  13.     /** 发送消息对象 */
  14.     private static $_send = [];
  15.     /**NotifyService Constructor
  16.      * @param array $msg  初始化消息数据
  17.      * $msg eg.["touser":uid,"method":all,"scene":register,"msg_type":text,"content":XXX ...];
  18.      * method:all表示全部推送,wechat表示微信推送,alipush表示阿里推送,mail表示邮件,getui表示个推
  19.      */
  20.     public static function send($msg){
  21.         $v = self::verify($msg);
  22.         if($v['code'] !== 200){
  23.             return ['code'=>$v['code'],'msg'=>$v['msg']];
  24.         }
  25.         emptyempty($msg['msg_type']) && $msg['msg_type'] = "text";  //默认文本消息,后期根据通道可以丰富推送消息类型,但微信仅支持文本!!!
  26.         self::$content = $msg;
  27.         self::buildMsg();
  28.         switch (self::$content['method']){
  29.             case "wechat":
  30.                 self::wechat_push(self::$_send);
  31.                 break;
  32.             case "alipush":
  33.                 self::mpush(self::$_send);
  34.                 break;
  35.             case "all":
  36.                 self::wechat_push(self::$_send['wechat']);
  37.                 self::mpush(self::$_send['alipush']);
  38.                 break;
  39.             default:
  40.                 break;
  41.         }
  42.         return ['code'=>"200",'msg'=>"消息发送成功"];
  43.     }
  44.     /**
  45.      * 发送微信模板消息
  46.      * @param array $msg buildMsg
  47.      */
  48.     private static function wechat_push($msg){
  49.         $wechat = &load_wechat('Receive');
  50.         $res = $wechat->sendTemplateMessage($msg);
  51.         return $res;
  52.     }
  53.     /**发送阿里移动推送
  54.      * @param array $msg buildMsg
  55.      */
  56.     private function mpush($msg){
  57.         $api = "cloudpush.aliyuncs.com";
  58.         date_default_timezone_set("GMT");
  59.         $dateTimeFormat = 'Y-m-d\TH:i:s\Z'; // ISO8601规范
  60.         $accessKeyId = sysconf('sms_aliyun_appid');
  61.         $accessKeySecret = sysconf('sms_aliyun_appsecret');
  62.         $msg['AccessKeyId'] = $accessKeyId;
  63.         $msg['SignatureMethod'] = "HMAC-SHA1";
  64.         $msg['Timestamp'] = date($dateTimeFormat);
  65.         $msg['SignatureVersion'] = "1.0";
  66.         $msg['SignatureNonce'] = uniqid();
  67.         $msg['Signature'] = self::getSignature($msg,$accessKeySecret);
  68.         //推送消息
  69.         $res = simplexml_load_string(HttpService::get($api,$msg));
  70.         $res = json_decode(json_encode($res),true);
  71.         //返回数据仅有RequestId,MessageId
  72.         if(!emptyempty($res['MessageId'])){
  73.             return ['code'=>200,'msg'=>"移动推送消息成功",'content'=>$res];
  74.         }else{
  75.             return ['code'=>500,'msg'=>$res['Message']];
  76.         }
  77.     }
  78.     /**
  79.      * 根据类型构建消息
  80.      */
  81.     private static function buildMsg(){
  82.         if(emptyempty(self::$content['method']) || !in_array(self::$content['method'],['wechat','alipush','mail','getui'])) {
  83.             return false;
  84.         }
  85.         switch (self::$content['method']){
  86.             case "wechat":
  87.                 self::$_send = ['touser'=>self::$content['openid'],'template_id'=>self::getTemplateId("wechat",self::$content['scene']),'url'=>"",'data'=>self::$content['content']];
  88.                 break;
  89.             case "alipush":
  90.                 self::$_send = [
  91.                     'Action'=>"Push",
  92.                     'RegionId' => "cn-hangzhou",
  93.                     'Version'=>"2016-08-01",
  94.                     'AppKey' => self::$content['app_key'],//创建的应用
  95.                     'PushType' => "NOTICE",
  96.                     'DeviceType' => "ALL",
  97.                     'Target' => "DEVICE",
  98.                     'TargetValue' => self::$content['device_id'],
  99.                     'Body' => self::$content['content'],
  100.                     'Title' => self::$content['title'],
  101.                     'JobKey' => self::$content['job_key'],  //如果需要后台记录推送消息状态
  102.                     'StoreOffline' => "true",
  103.                 ];
  104.                 break;
  105.             case "all":
  106.                 self::$_send['wechat'] = ['touser'=>self::$content['openid'],'template_id'=>self::getTemplateId("wechat",self::$content['scene']),'url'=>"",'data'=>self::$content['content']];
  107.                 self::$_send['alipush'] = [
  108.                     'Action'=>"Push",
  109.                     'RegionId' => "cn-hangzhou",
  110.                     'Version'=>"2016-08-01",
  111.                     'AppKey' => self::$content['app_key'],//创建的应用
  112.                     'PushType' => "NOTICE",
  113.                     'DeviceType' => "ALL",
  114.                     'Target' => "DEVICE",
  115.                     'TargetValue' => self::$content['device_id'],
  116.                     'Body' => self::$content['content'],
  117.                     'Title' => self::$content['title'],
  118.                     'JobKey' => self::$content['job_key'],  //如果需要后台记录推送消息状态
  119.                     'StoreOffline' => "true",
  120.                 ];
  121.                 break;
  122.             default:
  123.                 self::$_send = [];
  124.                 break;
  125.         }
  126.         return true;
  127.         //return self::$_send;
  128.     }
  129.     /**获取消息模板ID
  130.      * @param $chanel string 推送通道
  131.      * @param $scene string 消息场景
  132.      */
  133.     private static function getTemplateId($chanel,$scene){
  134.         //alarm:设备报警;remind:任务提醒;
  135.         $temp_wechat = ['register'=>"kFuKXjJlSWWeLj1MQelT7E0BRIf3H1gDBfm-5yF9Q30",'auth'=>"3jCOuTcVyfyb7bOJSm6HCO8kZaRdA8lnS0MQ_54QpEE","repass"=>"VOgt3a1SEKAeB8yN8f-hIFSRwMRM6W-TqvLPPTRLoMk","alarm"=>"hH7MhPQn5Bg3Kkc1DT4bMiqU38PrBryYTKUKyDjtlYU","remind"=>"Wm9TlfstyPMfqqmXiw0bOR-Mrw4qk8IKD6zlCBGKbhU"];
  136.         $temp_alipush = ['register'=>"",'auth'=>"","repass"=>"","alarm"=>"","remind"=>""];
  137.         $temp_getui = ['register'=>"",'auth'=>"","repass"=>"","alarm"=>"","remind"=>""];
  138.         return ${"temp_".$chanel}[$scene];
  139.     }
  140.     /** 阿里云签名
  141.      * @param array $parameters 请求参数
  142.      * @param string $accessKeySecret 接入密钥
  143.      * @return string
  144.      */
  145.     private static function getSignature($parameters$accessKeySecret){
  146.         ksort($parameters);
  147.         $canonicalizedQueryString = '';
  148.         foreach($parameters as $key => $value) {
  149.             $canonicalizedQueryString .= '&' . self::percentEncode($key)
  150.                 . '=' . self::percentEncode($value);
  151.         }
  152.         // 生成用于计算签名的字符串 stringToSign
  153.         $stringToSign = 'GET&%2F&' . self::percentencode(substr($canonicalizedQueryString, 1));
  154.         // 计算签名,注意accessKeySecret后面要加上字符'&'
  155.         $signature = base64_encode(hash_hmac('sha1', $stringToSign$accessKeySecret . '&', true));
  156.         return $signature;
  157.     }
  158.     /** percent加密
  159.      * @param $str
  160.      * @return string|null
  161.      */
  162.     private static function percentEncode($str){
  163.         // 使用urlencode编码后,将"+","*","%7E"做替换即满足ECS API规定的编码规范
  164.         $res = urlencode($str);
  165.         $res = preg_replace('/\+/', '%20', $res);
  166.         $res = preg_replace('/\*/', '%2A', $res);
  167.         $res = preg_replace('/%7E/', '~', $res);
  168.         return $res;
  169.     }
  170.     /** 数据验证
  171.      * @param array $arr 待验证数据
  172.      */
  173.     private static function verify($arr){
  174.         //验证的规则--备忘用
  175.         $rule = ['touser'=>"integer",'method'=>"in.all|alipush|wechat|mail|getui",'msg_type'=>"alpha",
  176.             'content'=>"require",'title'=>"require",'openid'=>"wechat.require",'device_id'=>"alipush.require",'app_key'=>"alipush.require",
  177.             'push_type'=>"alipush.MESSAGE|NOTICE"];
  178.         if(emptyempty($arr['touser']) || !is_numeric($arr['touser'])){return ['code'=>400197,'msg'=>"touser参数错误"];}
  179.         if(emptyempty($arr['method']) || !in_array($arr['method'],['all','alipush','wechat','mail','getui'])){return ['code'=>400201,'msg'=>"method参数错误"];}
  180.         if(emptyempty($arr['scene']) || !in_array($arr['scene'],["register","auth","repass","alarm","remind"])){return ['code'=>400202,'msg'=>"scene参数错误"];}
  181.         if(emptyempty($arr['content']) || !is_array($arr['content'])){return ['code'=>400203,'msg'=>"content参数错误"];}
  182.         if(emptyempty($arr['title']) || !is_string($arr['title'])){return ['code'=>400204,'msg'=>"title参数错误"];}
  183.         //String类型没有判断,开发时注意传入正确参数
  184.         if(in_array($arr['method'],['all','wechat']) && emptyempty($arr['openid'])){return ['code'=>400206,'msg'=>"openid参数错误"];}
  185.         if(in_array($arr['method'],['all','alipush']) && emptyempty($arr['device_id'])){return ['code'=>400207,'msg'=>"device_id参数错误"];}
  186.         if(in_array($arr['method'],['all','alipush']) && emptyempty($arr['app_key'])){return ['code'=>400208,'msg'=>"app_key参数错误"];}
  187.         if(in_array($arr['method'],['all','alipush']) && emptyempty($arr['push_type'])){return ['code'=>400209,'msg'=>"push_type为空"];}
  188.         if(in_array($arr['method'],['all','alipush']) && !in_array($arr['push_type'],['MESSAGE','NOTICE'])){return ['code'=>400210,'msg'=>"push_type参数错误"];}
  189.         return ['code'=>200,'msg'=>"参数校验成功"];
  190.     }
  191. }

上述源码仅测试了微信模板消息。阿里移动推送虽然集成了,但是并未进行测试。个推、邮件、短信是尚未开发的部分。

上述源码支持在波波thinkPHP开发的后端框架TP-admin中直接使用,也支持zoujingli的项目ThinkAdmin。

 

波波
你想把广告放到这里吗?

发表评论

您必须 登录 才能发表留言!