前言
编写日期 : 2022-11-04
写这篇文章原因
公司给政府做一个订餐系统,需要在员工在小程序上发起订餐后经过部门领导和书记的审批后,再由食堂确认订餐结果。在订餐审批单在各个节点流转的过程中,需要给每一个节点的审批人发送
微信订阅消息
和手机短信
,通知订餐流程所在节点的人有新的订餐审批单需要审批,最终将订餐结果通过微信订阅消息
和手机短信
反馈给订餐人员。手机短信那块后端很好整,没啥问题,但是这个微信小程序发送订阅消息这个没有整过,有点懵,但不慌。本来这个微信的消息订阅前端已经做好了,但是架构师说这块的逻辑后端做,所以我就来搞这个了。
官方文档说明
链接 :>>>微信小程序发送订阅消息官方文档 <<<
调用方式 HTTPS 调用
POST
https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN
请求参数
属性 | 类型 | 类型 | 说明 |
---|---|---|---|
access_token | String | 是 | 需要用小程序的appid和密钥secret去调用 官方 接口获取 |
template_id | String | 是 | 所需下发的订阅模板id |
page | String | 否 | 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转 |
touser | String | 是 | 接收者(用户)的 openid, |
data | String | 是 | 模板内容,格式形如 { “key1”: { “value”: any }, “key2”: { “value”: any } }的object |
miniprogram_state | String | 是 | 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 |
lang | String | 是 | 进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN |
申请的微信订阅消息模板
访问接口传参示例
{
"touser": "OPENID",
"template_id": "TEMPLATE_ID",
"page": "index",
"miniprogram_state":"developer",
"lang":"zh_CN",
"data": {
"thing1": {
"value": "金先生"
},
"time2": {
"value": "2020-05-01"
},
"phrase13": {
"value": "成功"
} ,
"thing17": {
"value": "xx地方"
}
}
}
代码
需要用到的当前小程序的参数
-
appid
小程序的appid -
secret
小程序密钥 -
TempId
申请的订阅消息模板id
获取access_token
@Value("${wx.xiaochengxu.appid}")
private String appid;
@Value("${wx.xiaochengxu.secret}")
private String secret;
/**
* @param appid secret
*/
@Override
public String getAccessToken() {
HttpResponse response = HttpRequest.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret + "").execute();
JSONObject tokenJson = JSON.parseObject(response.body());
String accessToken = tokenJson.get("access_token").toString();
return accessToken;
}
发送订阅消息
请求参数
public class SendMsgBody {
/*接收者(用户)的 openid*/
private String touser;
/*所需下发的订阅模板id*/
private String template_id;
/*点击消息后跳转的页面*/
private String page;
/*跳转小程序类型:developer 为开发版;trial 为体验版;formal 为正式版;默认为正式版*/
private String miniprogram_state="developer";
/*进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN返回值*/
private String lang="zh_CN";
/*模板数据,这里定义为object是希望所有的模板都能使用这个消息配置*/
private Object data;
public String getTouser() {
return touser;
}
public void setTouser(String touser) {
this.touser = touser;
}
//template_id、page、data ==>getset
}
请求参数中的data
public class MsgSuccess {
/*姓 名*/
private Map<String, String> thing1;
/*预约时间*/
private Map<String, String> time2;
/*预约状态*/
private Map<String, String> phrase13;
/*预约地点*/
private Map<String, String> thing17;
public Map<String, String> getThing1() {
return thing1;
}
public Map<String, String> getTime2() {
return time2;
}
public Map<String, String> getPhrase13() {
return phrase13;
}
public Map<String, String> getThing17() {
return thing17;
}
public void setThing1(String thing1) {
this.thing1 = getFormat(thing1);
}
public void setTime2(String time2) {
this.time2 = getFormat(time2);
}
public void setPhrase13(String phrase13) {
this.phrase13 = getFormat(phrase13);
}
public void setThing17(String thing17) {
this.thing17 = getFormat(thing17);
}
public HashMap<String, String> getFormat(String str) {
return new HashMap<String, String>() {{
put("value", str);
}};
}
@Override
public String toString() {
return "DCSuccess{" +
"thing1=" + thing1 +
", time2=" + time2 +
", phrase13=" + phrase13 +
", thing17=" + thing17 +
'}';
}
}
发送消息
@Value("${wx.xiaochengxu.sendMsg.successTempId}")
private String tempId;//模板id
@Override
public void wxSendMsgSuccess(String openId, Map<String, String> node) {
String accessToken = getAccessToken() ;
//先填充请求体中data的数据
MsgSuccess msgSuccess = new MsgSuccess ();
msgSuccess .setThing1(node.get("thing1"));
msgSuccess .setTime2(node.get("time2"));
msgSuccess .setPhrase13("成功");
msgSuccess .setThing17(node.get("thing17"));
//请求参数数据填充
SendMsgBody sendMsgBody = new SendMsgBody();
sendMsgBody.setTouser(openId);
sendMsgBody.setTemplate_id(tempId);
sendMsgBody.setData(msgSuccess);
String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken;
//发送请求
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(type);
HttpEntity<SendMsgBody> httpEntity = new HttpEntity<>(sendMsgBody, headers);
JSONObject jsonResult = restTemplate.postForObject(url, httpEntity, JSONObject.class);
JSONObject responseData = jsonResult ;
Integer errorCode = responseData.getInteger("errcode");
String errorMessage = responseData.getString("errmsg");
if (errorCode == 0) {
log.info("订餐通知消息发送成功");
} else {
log.info("订餐通知消息发送失败,errcode:{},errorMessage:{}", errorCode, errorMessage);
}
}
调用
@GetMapping("wxSendSuccess")
@ApiOperation(value = "微信小程序发送订阅消息通知")
public boolean wxSendSuccess(){
String openId = "oks*******************Gk";
HashMap<String, String> map = new HashMap<>();
map.put("thing1","***");
map.put("time2","2022-11-03 12:13");
map.put("thing17","405");
return wxService.wxSendMsgSuccess(openId,map);
}
结果
总结
咱们这个消息的订阅前端授权一次后,咱们也就只能发送成功一次,在接收订阅消息的用户没有同意接收订阅信息的时候,我们调用接口给用户发送订阅消息基本都会被拒绝。请求结果如下:
errcode:43101,errorMessage:user refuse to accept the msg rid: 6363495-2267c4de-7aa0fab1
如果你的小程序没有进行政府 医疗等认证,那你就不能长期订阅,所以每一次订阅消息的发送都需要接收方的一次授权,并且接收放的授权次数是可以累加的,比如说这次订阅了你没发订阅消息,下次再继续订阅,你就可以连续发两条订阅成功。但是在特殊情况下小程序方也会清楚所有的授权次数。