Dai Chong's blog

今天来一篇laravel5.5的小程序模板消息发送示例,总感觉用laravel框架写的代码逼格高了很多,奇怪!!

1.小程序向用户发送模板消息分为两步:
第一步:用户在小程序上进行表单操作(例如提交表单),此时前端要获取到form_id传递给后端
第二步:后端接收到from_id,再查询用户的openid,按照微信文档的数据格式进行发送
2.发送之前有一下几个前提:
第一:微信小程序以开通了消息服务
第二:微信小程序以配置了模板消息
第三:发送的数据内容必须和微信小程序配置的模板消息一致
第四:获取到正确的用户openid
第五:获取到未过期的微信access_token

首先是发送示例:

php代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<?php
class WxHelper
{
/**
* seedTempMessage 发送模板消息
* @author DaiChong
* @DateTime 2019-01-04
* @param array $seedData 需要发送的数据 格式为(key=>value)
* @param string $tempKey tempkey
* @return json
**/

public static function seedTempMessage($seedData,$tempKey)
{
if( empty($seedData) ){
self::seedError('请传输数据(格式为Array)!');
return;
}

//发送模板消息地址
$postUrl = config("company.seedTempMeg");
if( $postUrl == false ){
self::seedError('未定义微信模板消息地址(seedTempMeg)!');
return;
}

if($tempKey){
//查询消息模板内容
$temp = \App\Models\WechatMessageModel::where('temp_key',$tempKey)->first()->toArray();
if( empty($temp) ){
self::seedError('未添加此模板!');
return;
}
$seedData['template_id'] = $temp['temp_id'];
$temp_context = $temp['context'];
$temp_json = vsprintf($temp_context,$seedData);//替换数据

$temp_array = json_decode($temp_json,true);
//获取AccessToken
$accessToken = self::getAccessToken();
$postUrl = str_replace('ACCESS_TOKEN', $accessToken, $postUrl);
$curl = new \JSocket();
$curl->setUrl($postUrl);
$curl->setRetFormat(\JSocket::retFormatText);
foreach ($temp_array as $key => $value) {
$curl->setParam($key,$value);
}
$curl->setTimeout(30);
$curl->setHeader("Content-Type:application/json");
$curl->setRequestType(\JSocket::retFormatJson);
$curl->setMethod(\JSocket::methodPost);
$curl->exe();
$result = $curl->getRet();
self::seedError($result);
return $result;
}else{
self::seedError('未传入模板Key(tempKey)!');
return;
}
}
//获取微信AccessToken 我这里已经做了access_token处理,具体细节按照微信文档进行操作或评论
public static function getAccessToken(){
$access_token = \App\Data\WxAccessTokenData::info();
if ($access_token){
return $access_token;
}
$getAccessTokenUrl = config("company.getAccessToken");
$getAccessTokenUrl = str_replace(["APPID","APPSECRET"],[config("company.appid"),config("company.appsecret")],$getAccessTokenUrl);
$curl = new \JSocket();
$curl->setUrl($getAccessTokenUrl);
$curl->setRetFormat(\JSocket::retFormatJson);
$curl->setTimeout(30);
$curl->setMethod(\JSocket::methodGet);
$curl->exe();
$r = $curl->getRet();
return \App\Data\WxAccessTokenData::set($r["access_token"],$r["expires_in"]);
}
//发送失败
public static function seedError($msg){
\Log::info($msg);
return \ResponseHelper::success([
'code' => 1,
'message' => $msg
]);
}
}

数据配置:

配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"touser":"%s",
"template_id":"%s",
"page":"%s",
"form_id":"%s",
"data":{
"keyword1":{
"value":"%s"
},
"keyword2":{
"value":"%s"
},
"keyword3":{
"value":"%s"
},
"keyword4":{
"value":"%s"
},
"keyword5":{
"value":"%s"
}
}
}

这里配置也要和微信模板消息的配置一致,例如模板消息的配置内容最多到keyword8,那么这里就要在复制3个,”keyword5”:{“value”:”%s”}出来添加到后面,可以看到数据的内容都是用%s表示的,就是为了使用vsprintf替换数据。

发送配置:


发送配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//发送模板消息
public function postSeedTemp()
{
$request = \Request::all();
if( $request['openid'] == false || $request['form_id'] == false){
return \ResponseHelper::error("缺少参数!",NULL,NULL,500);
}
$array = array(
'touser' => $request['openid'],
'template_id' => '',
'page' => 'pages/home/home',
'form_id' => $request['form_id'],
'keyword1' => '卖家正在积极准备商品',
'keyword2' => '顺丰快递',
'keyword3' => '4324242342',
'keyword4' => 'S123456789',
'keyword5' => '你的物流状态已更新'
);
return \ResponseHelper::success([
'result' => \WxHelper::seedTempMessage($array,'logisticsMsg')
]);
}

可以看出这里的配置和数据配置是一一对应的。

curl模拟提交示例:


curl类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<?php
class JSocket {
const methodGet="GET"; //发起GET请求
const methodPost="POST"; //发起POST请求
const methodDelete="DELETE"; //发起DELETE请求
const methodPut="PUT"; //发起PUT请求

const retFormatJson="JSON"; //返回json格式
const retFormatXml="XML"; //返回XML格式
const retFormatText="Text"; //返回XML格式

private $ch; //链接对象

private $url; //请求地址
private $requestType; //请求方式
private $param=[]; //请求参数
private $timeout=10; //超时时间
private $header=[]; //请求头信息
private $method=JSocket::methodGet; //请求方式
private $file; //文件
private $ssl; //证书文件

private $ret; //返回结果
private $retFormat=JSocket::retFormatJson; //返回格式


public function __construct() {
$this->ch = curl_init();
}

public function setRetFormat($retFormat){
$this->retFormat=$retFormat;
return $this;
}

public function setRequestType($requestType)
{
$this->requestType=$requestType;
}

/**
* 设置请求地址
*/
public function setUrl($url){
$this->url=$url;
return $this;
}

/**
* 设置请求参数
* @param type $key
* @param type $value
* @return \JSocket
*/
public function setParam($key,$value){
$this->param[$key]=$value;
return $this;
}

/**
* 获取请求参数
* @param type $key
* @param type $value
* @return \JSocket
*/
public function getParam(){
return $this->param;
}

/**
* 设置请求超时时间
* @param type $timeout
* @return \JSocket
*/
public function setTimeout($timeout){
$this->timeout=$timeout;
return $this;
}

/**
* 设置请求头
* @param type $header
* @return \JSocket
*/
public function setHeader($header){
$this->header[]=$header;
return $this;
}

/**
* 设置请求方式
* @param type $method
* @return \JSocket
*/
public function setMethod($method){
$this->method=$method;
return $this;
}

/**
* 设置发送文件
* @param type $key
* @param type $value
* @return \JSocket
*/
public function setFile($key,$file){
$this->file[$key]=curl_file_create($file);
return $this;
}

/**
* 设置整数文件
* @param type $ssl
* @return \JSocket
*/
public function ssl($ssl){
$this->ssl=$ssl;
return $this;
}

/**
* 请求接口
* @return \JSocket
*/
public function exe(){
curl_setopt($this->ch, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
if($this->header){
curl_setopt($this->ch,CURLOPT_HTTPHEADER, $this->header);
curl_setopt($this->ch,CURLOPT_HEADER,FALSE);
}


switch ($this->method)
{
case self::methodPost:
curl_setopt($this->ch, CURLOPT_POST, TRUE);
if($this->file){
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $this->file);
}
if($this->param){
if ($this->requestType==self::retFormatJson){
curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($this->param));
}else if ($this->requestType==self::retFormatXml){
curl_setopt($this->ch, CURLOPT_POSTFIELDS, \DataBaseHelper::arrayToXml($this->param));
}else{
curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($this->param));
}
}
break;

case self::methodDelete:
curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
case self::methodGet:
if($this->param){
$this->url = $this->url . (strpos($this->url, '?') ? '&' : '?') . http_build_query($this->param);
}
}
if($this->ssl){
curl_setopt($this->ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($this->ch,CURLOPT_SSLCERT, $this->ssl['cert']);
curl_setopt($this->ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($this->ch,CURLOPT_SSLKEY, $this->ssl['key']);
curl_setopt($this->ch,CURLOPT_SSL_VERIFYPEER,TRUE);
curl_setopt($this->ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
curl_setopt($this->ch,CURLOPT_HEADER, FALSE);
if (isset($this->ssl['ca'])){
curl_setopt($this->ch,CURLOPT_CAINFO, $this->ssl['ca']);
}
}else{
curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, false);
}
curl_setopt($this->ch, CURLOPT_URL, $this->url);
$starttime=$this->microtimeFloat();
$this->ret = curl_exec($this->ch);
$endtime = $this->microtimeFloat();
$errorNum=curl_errno($this->ch);
$errorMsg=curl_error($this->ch);
$runtime = number_format(($endtime-$starttime), 4).'s';
if($errorNum){
Log::error($this->url, ["param"=>$this->param,"result"=>$errorNum,"runtime"=>$runtime,"response"=>$this->ret]);
Log::error("curl请求错误信息:".$errorMsg);
return NULL;
}
$this->http_code = curl_getinfo($this->ch,CURLINFO_HTTP_CODE);
curl_close ($this->ch);
Log::info($this->url, ["param"=>$this->param,"status"=>$this->http_code,"runtime"=>$runtime,"response"=>$this->ret]);
return $this;
}

/**
* 获取结果
*/
public function getRet(){
switch ($this->retFormat){
case JSocket::retFormatJson:
return json_decode($this->ret,TRUE);
break;
case JSocket::retFormatXml:
return \DataBaseHelper::xmlToArray($this->ret);
break;
}
return $this->ret;
}

public function getHttpCode(){
return $this->http_code;
}

/**
* 精确时间
* @return type
*/
public function microtimeFloat(){
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
}


 评论