之前工作需求,需要开发微信小程序,现在一段时间没做都快忘记了,今天记录一下!!!
首先,小程序的定时推送消息是有限制的,如果用户主动触发小程序1次,小程序可在7天内向用户主动推送1条模版消息,如果用户通过小程序完成支付,小程序可在7天内向用户主动推送3条模版消息,对于这句话的理解就是:用户触发一次,就会向后台发送一个formid来向用户推送模板消息,但是这个formid有效期只有7天,且只能使用一次。那么如何推送消息?又有什么要求呢?
首先,讲讲要求,想要推送模板消息,就需要用户的openid、小程序的accesstoken 和 触发表单的formid
官方文档说可以通过用户登录向后台发送 res.code 到后台换取 openId, sessionKey, unionId,利用提供的组件wx.login({}),直接上代码:
前端代码发送code给后台,返回openid,全局app.js
小程序代码:
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
var code = res.code;
if (code) {
wx.request({
url: ‘后台地址’,
method: "POST",
data: {
code: code,
},
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
success(res) {
that.globalData.openid = res.data.openid;//存储到全局
}
})
} else {
console.log('获取code失败')
}
}
})
后台PHP代码:
$code = $_POST['code'];//接收前端code
$APPID = "APPID";//小程序后台管理提供的id
$APPSECRET = "APPSECRET";//小程序后台管理提供的秘钥,很重要,首次生成后记好,不要随便更改,如果重新生成秘钥后,需要修改已发布的项目,否则会报错
/**
* openid
*/
function getOpenId($APPID,$APPSECRET,$code) {
//$url是小程序提供的接口
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=".$APPID."&secret=".$APPSECRET."&js_code=".$code."&grant_type=authorization_code";
$res = httpRequest($url);
$res = json_decode($res,true);
return $res;
}
/**
* curl获取指定网页内容
*/
function httpRequest($url, $data='', $method='GET'){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
if($method=='POST')
{
curl_setopt($curl, CURLOPT_POST, 1);
if ($data != '')
{
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
}
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
curl_close($curl);
return json_decode($result,true);
}
注意:目前小程序的access_token有效期期是2小时,也就是7200秒,所以需要定时刷新,重复刷新会导致上次获取的 access_token 失效,建议开发者统一获取和刷新 access_token,不应该各自去刷新,否则容易造成冲突,导致 access_token 覆盖而影响业务;
php获取accesstoken:
/**
* 获取accessToken
*/
function accessToken($APPID,$APPSECRET) {
$tokenFile = __DIR__."xxxx.txt";//存储access_token的文件
//缓存文件名
$data = json_decode(file_get_contents($tokenFile));
//判断时间是否过期或不存在
if ($data->expire_time < time() || !$data->expire_time) {
//小程序提供的获取accesstoken的接口
$access_token = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$APPID."&secret=".$APPSECRET;
$res = httpRequest($access_token);
$res = json_decode($res,true);
$access_token = $res['access_token'];
if ($access_token) {
//设置过期时间
$jsonstr['expire_time'] = time() + 7000;
//设置 $access_token
$jsonstr['access_token'] = $access_token;
$fp = fopen($tokenFile, "w");//打开文件
fwrite($fp, json_encode($jsonstr));//写入文件
fclose($fp);//关闭文件
}
} else {
//未过期直接从文件中读取accesstoken
$access_token = $data->access_token;
}
return $access_token;
}
$token = accessToken($APPID,$APPSECRET);
既然是表单的id,当然就是form+button了,直接上代码
小程序代码:
//wxml代码
<form bindsubmit="getformid" report-submit="{{true}}">
<button formType="submit" bindtap='close' data-title='hb' class='HB_des'>取消</button>
</form>
//js代码
//获取用户唯一标识 formid,推送模板消息
getformid: function (e) {
if (e.detail.formId != "the formId is a mock one") {
var formId_str = e.detail.formId;
wx.request({
url: '后台地址',
method: 'POST',
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
formid: formId_str,
openid: app.globalData.openid,//用户id,上面获取
expire: parseInt(new Date().getTime() / 1000) + 604800,//计算7天后的过期时间时间戳-秒,存储需要
},
success: function (res) {
console.log('成功');
console.log(res);
},
fail: function (err) {
console.log('request fail ', err);
},
})
}
},
该获取的都获取了,现在来PHP推送模板消息
$formid= $_POST['formid'];
$openid= $_POST['openid'];
$token= $_POST['token'];//accesstoken不需要返回前端,在后直接使用,这里为了方便,获取请看上文
/**
* 发送模板消息
*/
function sendTemMsg($formid,$openid,$token){
//小程序提供发布模板的接口
$url='https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token='.$token;
$temid='Dwme7pfbGkfh87394p0tkOrYhKpMbeok27CFP3-BDjU';//小程序后台管理模板id,下文介绍
$page='pages/index/index';//小程序首页
//以下提供的模板样式,根据项目需求,手动添加
$data = array(
"touser"=>$openid,
"template_id"=>$temid,
"page"=>$page,
"form_id"=>$formid,
"data"=>array(
"keyword1"=>array(
"value"=>'自定义内容',
"color"=>"#173177"
),
"keyword2"=>array(
"value"=>'好文章分享!',
"color"=>"#173177"
),
"keyword3"=>array(
"value"=>'好文章分享!',
"color"=>"#173177"
),
// "keyword4"=>array(
// "value"=>'北京',
// "color"=>"#173177"
// ),
),
// "emphasis_keyword"=>"keyword1.DATA",//需要进行加大的消息
);
$jsondata=json_encode($data);
$res=httpRequest($url, $jsondata,'POST');
if($res){
//推送模板消息成功
return json_encode(array('state'=>4,'msg'=>$res));
}else{
return json_encode(array('state'=>5,'msg'=>$res));
}
}
if($formid){
echo sendTemMsg($formid,$openid,$token);
}else{
echo '返回数据失败';
}
到这里推送模板消息基本就结束啦!当用户触发的时候就可以向用户推送消息了,新手可能不知道上图中的小程序后台管理模板是什么,现在解释一下,小程序后台管理模板,需要登入微信公众号平台https://mp.weixin.qq.com,找到模板消息栏目–>进行自定义模板–》完成后就会生成自定义模板ID,如下图:
以下内容用于正常推送消息,不能乱用否则影响用户体验
当我们有业务需求,需要向用户定时推送,一天只能推送一次,那么我们就可以搜集formid,但是formid有效期是7天,一个只能使用一次,所有我们只需要收集7个就好,现在有两种方法:
**方法一:**所有和用户交互的地方使用form+button进行改造,到时工作量有点大,正常情况也不需要那么多的formid。
**方法二:**就是form+button相互嵌套进行改造,嵌套个7层,调整样式,也能获得7个formid.
这里使用的是方法二,代码走起
//wxml代码
<form bindsubmit="getformid" report-submit="{{true}}" class='HB_form'>
<button formType="submit" class='HB_btn'>
<form bindsubmit="getformid" report-submit="{{true}}">
<button formType="submit" class='HB_btn'>
<form bindsubmit="getformid" report-submit="{{true}}">
<button formType="submit" class='HB_btn'>
<form bindsubmit="getformid" report-submit="{{true}}">
<button formType="submit" class='HB_btn'>
<form bindsubmit="getformid" report-submit="{{true}}">
<button formType="submit" class='HB_btn'>
<form bindsubmit="getformid" report-submit="{{true}}">
<button formType="submit" class='HB_btn'>
<form bindsubmit="getformid" report-submit="{{true}}">
<button formType="submit" bindtap='close' data-title='hb' class='HB_des'>获取formid</button>
</form>
</button>
</form>
</button>
</form>
</button>
</form>
</button>
</form>
</button>
</form>
</button>
</form>
//wxss代码
.HB_form{
position: relative;
height: 44px;
width: 100%;
display: inline-block;
}
.HB_btn {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 0;
border-radius:0;
padding: 0;
line-height: 0px;
overflow: visible;
}
.HB_des {
position: absolute;
top: 0;
left: 0;
border-radius:0;
line-height: 44px;
padding: 0;
width: 100%;
}
这样做的目的就是利用点击穿透,点击1次相当于点击7次,就能获得7个formid,然后在在发送给后台存储
改造js
//js代码
//获取用户唯一标识 formid,推送模板消息
getformid: function (e) {
if (e.detail.formId != "the formId is a mock one") {
this.data.formids.push(e.detail.formId);
if (this.data.formids.length == 7) {
var formId_arr = this.data.formids;
var formId_str = JSON.stringify(formId_arr);
this.data.formids = [];
wx.request({
url: '后台地址',
method: 'POST',
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
formid: formId_str,//获取的formid数组
openid: app.globalData.openid,//用户id
expire: parseInt(new Date().getTime() / 1000) + 604800,//计算7天后的过期时间时间戳-秒
},
success: function (res) {
console.log('成功');
console.log(res);
},
fail: function (err) {
console.log('request fail ', err);
},
})
}
}
},
//PHP代码
//php存储formid代码
$formid= $_POST['formid'];//表单ID-数组
$openid= $_POST['openid'];//用户ID
$expire= $_POST['expire'];//7天后的过期时间
if($openid){
//以键值对的方式进行存储formid和过期时间,方便判断formid是否过期
$arr=array($expire=>$formid);
$data=json_encode($arr);
//存储到数据库,根据项目的数据库进行存储
$aa=redis()->hset('formid', $openid, $data);
echo $aa;//插入成功返回1,替换成功或失败返回0
}else{
echo '没有openid';
$aa=redis()->hgetall('formid');
var_dump($aa);
}
定时读取数据库的formid来推送消息
001、php定时脚本
//读取数据库中formid
$aa=redis()->hgetall('formid');
if($aa){
foreach ( $aa as $key_openid => $value) {
$openid=$key_openid;
$arr=json_decode($value,true);//获取用户id对应的formid和过期时间
foreach($arr as $key_time=>$value_formid){
$expire=$key_time;
$formid_arr=json_decode($value_formid,true);//获取formid
if($expire>time() && count($formid_arr,0)>0){//查看是否过期
$formid=array_shift($formid_arr);//读取并删除第一个formid
sendTemMsg($formid,$openid,$token);//下发模板消息,sendTemMsg函数见上文
if($formid_arr){//判断是否还有formid,有就重新存储,没有就删除对应用户,清理内存
$formid_str=json_encode($formid_arr);
$jsonarr=array($expire=>$formid_str);
$data=json_encode($jsonarr);
redis()->hset('formid', $openid, $data);
break 1;
}else{
redis()->hdel('formid',$openid);
break 1;
}
}
}
}
}
Linux服务器上使用CronTab定时执行php
首先,进入命令行模式,在命令行中键入:
crontab -e
00 * * * * lynx -dump https://www.yourdomain.com/script.php
至于这些代码解释自行百度,推荐以下文章有解释CronTab定时执行php
设置定时任务:
https://blog.csdn.net/zalan01408980/article/details/80555492
PHP实现定时任务的几种方法:
https://www.cnblogs.com/starFeeling/p/6751921.html