微信小程序发送订阅消息|模板消息

~~~~ 本文仅针对新手小白同学,老鸟请略过  ~~~

对于初学者,找文档也是个麻烦事,因为文档太大了。故本文把官方文档做了提炼和引用,在每一个接口处

目录

一、需求场景

二、新手的几个问题

三、微信小程序准备知识

1、关于订阅

1)订阅长什么样?

2)为什么要订阅?

3)什么时候提示订阅?

4)订阅类型

5)订阅后用户如何查看或管理

2、简要说明几个参数

四、配置消息模板

五、获取重要的参数

1、获取openid

第1步:前台获取 code

第2步:后台获取 openid

2、获取access_token

六、引导用户完成订阅

1、调起客户端小程序订阅消息界面

2、查询用户当前设置(非必须,含订阅信息)

七、阶段小结

八、步入正题发送消息

九、功能扩展与项目集成

         1、若干问题

2、简单设计参考


一、需求场景

在微信小程序项目中,经常有消息需要实时发送用户。如流程任务审核、付款到账、交易通知、工单提醒等。有两种实现方法:

一、微信公众号绑定小程序,用户关注公众号。系统发送消息到微信公众号。这种情况需要你额外准备一个公众号。

二、用户订阅小程序通知,订阅后系统直接发消息到微信小程序。消息在” 微信->服务号->通知“ 查看。

 本文采用并推荐使用第二种方法。

二、新手的几个问题

Q1、系统如何识别某用户的微信,如系统用户名“张三”,对应微信标识是哪个?

Q2、微信是否允许我给张三发通知?我能够一直给张三发通知吗?

Q3、我是否可以自定义内容模板?别人定义的模板我想用,但不需要那么多参数怎么办?

Q4、用户点击我发的通知消息,能够跳转到对应的业务办理页面吗?

这些问题会贯穿以下内容

三、微信小程序准备知识

1、关于订阅

1)订阅长什么样?

PS: 大家几乎天天用小程序,每个新小程序都会提示,见到此图并不会陌生

2)为什么要订阅?

微信规定,只有用户自主订阅后,系统后台才能发送消息给用户。很好理解,防止垃圾信息骚扰用户。

3)什么时候提示订阅?

我们先从产品体验角度,讨论下什么时候弹出订阅界面比较合适。

A. 如果是开放式产品不需要严格的权限(如商城、点餐等),用户需要先浏览体验下内容等。那么可以在用户点击下单,或需要确认身份(注册/登录系统)后,触发弹出订阅界面。

B. 如果是权限很严格的,小程序首先进行登录界面,那么登录成功后,跳到主页前适合触发弹出订阅界面。

4)订阅类型

一次订阅:用户订阅后,在任意时间发一次消息给用户。不能发第二次。那么如何发多条消息呢?需要用户在弹出的订阅界面:

勾选 “○总是保持以上选择” 即可。

长期订阅:用户订阅后,可长期发送多条信息。但对行业与资质要求较高,准入门槛高,除非你是toG的或者民生类、教育类等等。

5)订阅后用户如何查看或管理

旁白:如果用户未订阅,系统不要浪费表情和系统资源为其发送。

用户可能从来没用这个小程序,也可能是订阅时给拒绝了。总之ta收不到。

所以我们系统后台

1、需要记录用户的订阅行为,细化到订阅了哪个模板。

2、甚至取消时,我们也可接收到微信系统通知,随时更新标记。

2、简要说明几个参数

appid、app_secret: 系统为每个小程序分配的唯一标识,和密钥。登录小程序后台:管理->开发管理->开发设置,查看并妥善保存。

code: 微信分配的临时登录凭证

access_token: 小程序全局唯一后台接口调用凭据,token有效期为7200s,过期了需要调接口刷新。

openId:  一个微信号关注了一个公众号/小程序,给该微信用户生成的唯一编号就是openId。这是我们能够找到用户的重要标识。

unionId: 一个微信开放平台下的多个应用,包括多个公众号/小程序等,针对同一个微信用户生成的unionId是相同的。

templageId: 开发者在小程序后台,选取或申请了模板后,生成的模板唯一标识。

以上这些参数,是微信生态体系的核心和基础参数。不仅仅是发送消息使用,在其它任意调用API之处均需要使用。

四、配置消息模板

登录微信小程序后台,基础功能 -> 订阅消息,优先从公共模板库中查找接近自己的模板,并从中选择关键词

从我的模板中,复制模板ID使用。订阅以及消息发送里,都需要模板ID


 

五、获取重要的参数

我们看微信官方文档,发送消息需要以下参数,我们倒推法逐一得到这些参数:

 发送订阅消息 | 微信开放文档微信开发者平台文档icon-default.png?t=O83Ahttps://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/sendMessage.html

属性类型必填说明
access_tokenstring接口调用凭证,该参数为 URL 参数,非 Body 参数。使用access_token或者authorizer_access_token
template_idstring所需下发的订阅模板id
pagestring点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转
touserstring接收者(用户)的 openid
datastring模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }的object
miniprogram_statestring跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
langstring进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN

1、获取openid

第1步:前台获取 code

twx.login(Object object) | 微信开放文档微信开发者平台文档icon-default.png?t=O83Ahttps://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html

wx.login({
  success (res) {
    if (res.code) {
      //发起网络请求
      wx.request({
        url: 'https://example.com/onLogin',
        data: {
          code: res.code
        }
      })
    } else {
      console.log('登录失败!' + res.errMsg)
    }
  }
})

uniapp可以这样用

uni.login(OBJECT) | uni-app官网uni-app,uniCloud,serverless,uni.login(OBJECT),App平台支持的登录方式,小程序平台支持的登录方式,web平台支持的登录方式,OBJECT 参数说明,注意事项,uni.getLoginCode(OBJECT),uni.checkSession,uni.geicon-default.png?t=O83Ahttps://uniapp.dcloud.net.cn/api/plugins/login.html#login

uni.login({
  provider: 'weixin', //使用微信登录
  success: function (loginRes) {
    console.log(loginRes.authResult);
  }
});

第2步:后台获取 openid

小程序登录 | 微信开放文档微信开发者平台文档icon-default.png?t=O83Ahttps://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html

GET 
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code 

respone:
{
"openid":"xxxxxx",
"session_key":"xxxxx",
"unionid":"xxxxx",
"errcode":0,
"errmsg":"xxxxx"
}
    
    public static final String WECHAT_OPENID_URL = "https://api.weixin.qq.com/sns/jscode2session?";
    /**
     * 获取微信 openId unionId
     *
     * @param appId
     * @param code
     * @return
     */
    public WechatSession code2Session(String companyNo, String appId, String code) {

        String appSecret = getAppSecret(companyNo, appId);

        StringBuffer request = new StringBuffer();
        request.append(WECHAT_OPENID_URL);
        request.append("appid=").append(appId).append("&");
        request.append("secret=").append(appSecret).append("&");
        request.append("js_code=").append(code).append("&");
        request.append("grant_type=").append("authorization_code");
        //log.info("wechat request jscode2session url={}", request);

        try {
            String response = HttpUtil.get(request.toString());
            log.info("wechat response: {}", response);

            JSONObject result = JSON.parseObject(response);
            Integer errCode = result.getInteger("errcode");
            if(errCode == null){
                WechatSession session = new WechatSession();
                session.setOpenId(result.getString("openid"));
                session.setUnionId(result.getString("unionid"));
                session.setSessionKey(result.getString("session_key"));
                return session;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }

2、获取access_token

获取接口调用凭据 | 微信开放文档微信开发者平台文档icon-default.png?t=O83Ahttps://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html

GET 
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

response
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200
} 
    
    public static final String WECHAT_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?";

    /**
     * 获取微信 access_token
     *
     * @param appId
     * @param companyNo
     * @return
     */
    public String acccessToken(String companyNo, String appId) {

        String appSecret = getAppSecret(companyNo, appId);

        StringBuffer request = new StringBuffer();
        request.append(WECHAT_TOKEN_URL);
        request.append("appid=").append(appId).append("&");
        request.append("secret=").append(appSecret).append("&");
        request.append("grant_type=").append("client_credential");
        //log.info("wechat request token url={}", request);

        try {
            String response = HttpUtil.get(request.toString());
            log.info("wechat response: {}", response);

            JSONObject result = JSON.parseObject(response);
            Integer errCode = result.getInteger("errcode");
            if(errCode == null){
                String accessToken = result.getString("access_token");
                return accessToken;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }

六、引导用户完成订阅

1、调起客户端小程序订阅消息界面

wx.requestSubscribeMessage(Object object) | 微信开放文档微信开发者平台文档icon-default.png?t=O83Ahttps://developers.weixin.qq.com/miniprogram/dev/api/open-api/subscribe-message/wx.requestSubscribeMessage.html

wx.requestSubscribeMessage({
  tmplIds: ['i6Mr7Qlw-w3KAxRz0ol84rPTXmvCoFwJgIHOw1tbiAI','nq42vjDBz4FgSRdHeYY32qYrfM1i16cf0uTSRGpr96M'],
  success (res) { 
    //可传给后台保存
  }
})

success回调参数

{ 
errMsg: "requestSubscribeMessage:ok", 
zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: "accept", 
abc-aaaa-bcUUwjllajcQyW-edafCVvzPkK4de2a: "reject"
} 

uniapp可以这样用

uni.requestSubscribeMessage(Object object) | uni-app官网uni-app,uniCloud,serverless,uni.requestSubscribeMessage(Object object)icon-default.png?t=O83Ahttps://uniapp.dcloud.net.cn/api/other/requestSubscribeMessage.html#requestsubscribemessage

uni.requestSubscribeMessage({
  tmplIds: [''],
  success (res) { }
})

调用成功后,最好送到后台进行保存。不然你可能需要如下方法去反查。。。

2、查询用户当前设置(非必须,含订阅信息)

wx.getSetting(Object object) | 微信开放文档微信开发者平台文档icon-default.png?t=O83Ahttps://developers.weixin.qq.com/miniprogram/dev/api/open-api/setting/wx.getSetting.html

wx.getSetting({
  withSubscriptions: true,
  success (res) {
    console.log(res.authSetting)
    // res.authSetting = {
    //   "scope.userInfo": true,
    //   "scope.userLocation": true
    // }
    console.log(res.subscriptionsSetting)
    // res.subscriptionsSetting = {
    //   mainSwitch: true, // 订阅消息总开关
    //   itemSettings: {   // 每一项开关
    //     SYS_MSG_TYPE_INTERACTIVE: 'accept', // 小游戏系统订阅消息
    //     SYS_MSG_TYPE_RANK: 'accept'
    //     zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: 'reject', // 普通一次性订阅消息
    //     ke_OZC_66gZxALLcsuI7ilCJSP2OJ2vWo2ooUPpkWrw: 'ban',
    //   }
    // }
  }
})

七、阶段小结

 通过以上步骤,获得了发送消息的必要条件。有配置的工作,也有代码的工作。代码有台前调用微信的,还有后台调用微信的,注意区分。

八、步入正题发送消息

发送订阅消息 | 微信开放文档微信开发者平台文档icon-default.png?t=O83Ahttps://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/sendMessage.html


/*
官方给出的模板内容格式
姓名: {{name01.DATA}}
金额: {{amount01.DATA}}
行程: {{thing01.DATA}}
日期: {{date01.DATA}}
*/

{
  "touser": "OPENID",
  "template_id": "TEMPLATE_ID",
  "page": "index",
  "data": {
      "name01": {
          "value": "某某"
      },
      "amount01": {
          "value": "¥100"
      },
      "thing01": {
          "value": "广州至北京"
      } ,
      "date01": {
          "value": "2018-01-01"
      }
  }
}
    
    public static final String WECHAT_SENDMSG_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?";  

  /**
     * 发送订阅消息
     * @param accessToken
     * @param templateId
     * @param page
     * @param openId
     * @param data
     * @return
     */
    public boolean sendMessage(String accessToken, String templateId, String page, String openId, Object data){

        JSONObject param = new JSONObject();
        param.put("template_id", templateId);
        param.put("page", page);         //用户点击消息,跳转的页面,可携带参数
        param.put("touser", openId);
        param.put("data", data);
        param.put("miniprogram_state", "developer");
        param.put("lang", "zh_CN");

        log.info("wechat request sendMessage param={}", param.toJSONString());
    
        String url = WECHAT_SENDMSG_URL + "access_token=" + accessToken;

        try {
            String response = HttpUtil.post(url, param.toJSONString());
            log.info("wechat response: {}", response);

            JSONObject result = JSON.parseObject(response);
            Integer errCode = result.getInteger("errcode");
            if(errCode == 0){
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

九、功能扩展与项目集成
1、若干问题

1)、如何通过微信OpenId找到系统用户?最好不要侵入原来的用户体系设计

参考思路:对于后台已经开通的用户,在首次登录时引导用户输入用户名、密码或其它标识,小程序客户端口自动获取到openid,完成openid与系统用户的绑定。

2)、access_token获取后保存到哪里?怎么判断过期了,如何刷新?

将access_token保存到微信应用表中,或redis缓存中。根据获得时间+7200秒,设置过期时间

3)、用户是否订阅了某个消息模板?只有用户成功订阅,系统才发送给该用户消息

将订阅信息保存到微信用户表中,订阅完成时保存这些信息。如果用户在订阅后期,主动关闭了订阅,微信会发信息给平台。可以随时更新订阅信息

4)、业务系统:某类消息模板需要发送到哪些用户?

将某类业务(对应一个模板),与需要发送的系统用户或微信用户,通过管理系统配置并记录。当然也可以通过某类规则,完成多个用户的关联。

2、简单设计参考

1) t_wechat_app 微信应用表

字段名称

字段描述

ID

主键ID

create_by

创建人

create_time

创建日期

update_by

更新人

update_time

更新日期

app_id

应用编号

app_name应用名称
app_secret        密钥

access_token

令牌

token_exp_time

令牌失效时间

remarks

备注信息

2) t_wechat_user 微信用户表

字段名称

字段描述

ID

主键ID

create_by

创建人

create_time

创建日期

update_by

更新人

update_time

更新日期

app_id

应用编号

openid

OPENID

unionid

UNIONID

nick_name

用户昵称

avatar_url

头像

session_key

SessionKey

subscribe_template

订阅消息模板

sys_user_id

系统用户ID

username

登录用户名

status

状态

remarks

备注信息

3) t_wechat_message_user 微信消息成员表

字段名称

字段描述

ID

主键ID 

create_by

创建人

create_time

创建日期

update_by

更新人

update_time

更新日期

company_no

公司编号

app_id

应用编号

template_id

消息模板

customer_id

所属商户

sys_user_ids

发送用户

status

状态

remarks

备注

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值