手把手教你实现微信小程序向特定用户推送一次性订阅消息

这篇具有很好参考价值的文章主要介绍了手把手教你实现微信小程序向特定用户推送一次性订阅消息。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

小程序、公众号准备

目前有一个已微信认证订阅号类型公众号,一个微信认证小程序,小程序和公众号互相关联。尚不清楚是否必须微信认证或特定类型,因为目前没遇到类型不匹配或相关的问题,发送微信小程序一次性订阅消息的相关限制较少

微信小程序订阅消息,微信小程序,小程序,订阅消息
微信小程序订阅消息,微信小程序,小程序,订阅消息
微信小程序订阅消息,微信小程序,小程序,订阅消息

什么是小程序订阅消息

1、功能介绍

  • 订阅消息推送位置:服务通知
    微信小程序订阅消息,微信小程序,小程序,订阅消息

  • 订阅消息下发条件:用户自主订阅
    微信小程序订阅消息,微信小程序,小程序,订阅消息

  • 订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面
    微信小程序订阅消息,微信小程序,小程序,订阅消息

2、消息类型

  • 一次性订阅消息(本文实现的消息类型
    一次性订阅消息用于解决用户使用小程序后,后续服务环节的通知问题。用户订阅后,开发者可不限时间地下发一条对应的服务消息;每条消息可单独订阅或退订。

  • 长期订阅消息
    一次性订阅消息可满足小程序的大部分服务场景需求,但线下公共服务领域存在一次性订阅无法满足的场景,如航班延误,需根据航班实时动态来多次发送消息提醒。为便于服务,我们提供了长期性订阅消息,用户订阅一次后,开发者可长期下发多条消息。

    目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务。

  • 设备订阅消息
    设备订阅消息是一种特殊类型的订阅消息,它属于长期订阅消息类型,且需要完成「设备接入」才能使用。

    设备订阅消息用于在设备触发某些需要人工介入的事件时(例如设备发生故障、设备耗材不足等),向用户发送消息通知。详见设备订阅消息文档。

详细介绍还是自己看官方文档吧:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/subscribe-message.html

实现过程

1、订阅消息模板选用

  • 登录小程序微信公众平台,点击功能——订阅消息

    微信小程序订阅消息,微信小程序,小程序,订阅消息

  • 点击公共模板库,搜索选用需要的模板

    微信小程序订阅消息,微信小程序,小程序,订阅消息

  • 只能选用5个关键词,输入场景说明后点击提交

    微信小程序订阅消息,微信小程序,小程序,订阅消息

  • 如果没有自己想要的关键词,点击页面上的点击申请去申请自己需要的关键词。注意,每个月有5次申请机会,申请的关键词是你要新增的关键词,原本模板中有的就不要再重复申请了,申请通过后点选用模板可以选用原本有的关键词和你申请通过的关键词

    微信小程序订阅消息,微信小程序,小程序,订阅消息

微信小程序订阅消息,微信小程序,小程序,订阅消息

  • 选好模板关键词确认提交后,在我的模板下就有了
    微信小程序订阅消息,微信小程序,小程序,订阅消息
  • 点击详情可以查看使用该模板发送订阅消息需要传什么参数
    微信小程序订阅消息,微信小程序,小程序,订阅消息

2、对接拉起小程序登录弹窗,解析获取用户openId,将openId保存到数据库,关联到用户信息或用户表加个字段

  • 小程序部分实现

官方文档:https://uniapp.dcloud.net.cn/api/plugins/login.html

uni.login({
  success: (result) => {
  	//保存获取到的code
    this.jsCode = result.code; 
    request({
      url: '后台接口地址',
      method: "POST",
      data: { code: result.code },
    })
      .then((res) => {
        this.openid = res.data.openid;
        this.session_key = res.data.session_key;
      })
      .catch((err) => {});
  },
  fail: (error) => {},
});
  • 后端部分实现

官方文档:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html

// 对传入code进行解密获取openid
LinkedMultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("appid",appid);
params.add("secret",secret);
// 小程序调接口传的code
params.add("js_code",code);
params.add("grant_type","authorization_code");

String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+appid+"&secret="+secret+"&js_code="+code+"&grant_type=authorization_code";
String result = restTemplate.getForObject(url, String.class);
JSONObject jsonObject = JSON.parseObject(result);

Map<String, String> map = new HashMap<>();
try {
    map.put("openid", jsonObject.get("openid").toString());
    map.put("session_key", jsonObject.get("session_key").toString());
    log.info("code解密成功");
} catch (Exception e){
    log.error("code解析失败,"+jsonObject.getString("errmsg"));
    throw new RuntimeException("code解析失败,"+jsonObject.getString("errmsg"));
}
return map;

3、引导用户主动授权订阅消息发送

  • 在小程序新建appletAuthorize.js文件

官方文档:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/subscribe-message/wx.requestSubscribeMessage.html

import Vue from 'vue'

const templateIds = [
	// 审批结果通知
	'审批结果通知模板id',
	// 领用单领用通知
	'领用单领用通知模板id',
	// 待审核通知
	'待审核通知模板id',
	// 数据报表生成通知
	'数据报表生成通知模板id',
	// 工单完成提醒
	'工单完成提醒模板id'
]

const appletAuthorize = () => {
	let tmplIds = []
	uni.getStorage({
		key: "currAuthorizeStep",
		success: ({
			data
		}) => {
			console.log("currAuthorizeStep", data);
			let temp = data
			for (var i = 0; i < 3; i++) {
				if (temp === templateIds.length) {
					temp = 0
				}
				tmplIds.push(templateIds[temp])
				temp++
			}
			uni.setStorage({
		 	key: "currAuthorizeStep",
				data: temp,
				success: (result) => {},
				fail: (error) => {},
			});
			if (tmplIds.length === 0) {
				return
			}

			wx.requestSubscribeMessage({
				tmplIds: tmplIds,
				success(res) {
					console.log("订阅消息唤起成功 =====>", res)
				},
				fail(err) {
					console.log("订阅消息唤起失败 =====>", err)
				}
			})
		},
		fail: (error) => {},
	});
};

Vue.prototype.$appletAuthorize = appletAuthorize

export default appletAuthorize;

注意:

(1)在登录时候setStorage

uni.setStorage({
  key: "currAuthorizeStep",
  data: 0,
  success: (result) => {},
  fail: (error) => {},
});

(2)用templateIds把模板id都定义出来,每次取三个授权,是因为wx.requestSubscribeMessage订阅的消息模板的id每次最多只能传3个,不然会订阅失败

  • 在小程序页面@click的方法里面加上this.$appletAuthorize()
    微信小程序订阅消息,微信小程序,小程序,订阅消息
    注意:
    (1)用户对订阅消息授权了才能向用户发订阅消息
    (2)用户发生点击行为或者发起支付回调后,才可以调起订阅消息界面。即不能一进入页面就调用订阅授权方法,需要和点击事件绑定一起使用

4、订阅消息模板有了,用户openId有了,用户订阅消息授权有了,接下来是发送订阅消息了

官方文档:
(1)https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/sendMessage.html
(2)https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html

参数拼接工具类

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

/**
 * 发送订阅消息参数整合
 *
 * @author Administrator
 */
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Slf4j
public class AppletParamUtil {

    /**
     * 待审核通知参数
     * @param thing1Value
     * @param thing11Value
     * @param thing12Value
     * @param time4Value
     * @param thing10Value
     * @return
     */
    public static Map<String, Object> getNeedConfirmParam (String thing1Value,
                                                           String thing11Value,
                                                           String thing12Value,
                                                           String time4Value,
                                                           String thing10Value) {

        Map<String, Object> objMap = new HashMap<>(5);
        Map<String, String> map = new HashMap<>(1);
        // 申请人
        map.put("value", thing1Value);
        objMap.put("thing1", map);
        // 申请人部门
        map = new HashMap<>(1);
        map.put("value", thing11Value);
        objMap.put("thing11", map);
        // 申请人工种
        map = new HashMap<>(1);
        map.put("value", thing12Value);
        objMap.put("thing12", map);
        // 申请时间
        map = new HashMap<>(1);
        map.put("value", time4Value);
        objMap.put("time4", map);
        // 申请物品
        map = new HashMap<>(1);
        map.put("value", thing10Value);
        objMap.put("thing10", map);

        return objMap;
    }
    
    /**
     * 数据报表生成通知
     * @param thing1Value
     * @param thing2Value
     * @return
     */
    public static Map<String, Object> getDataReportParam (String thing1Value, String thing2Value) {

        Map<String, Object> objMap = new HashMap<>(2);
        Map<String, String> map = new HashMap<>(1);
        // 报表名称
        map.put("value", thing1Value);
        objMap.put("thing1", map);
        // 数据统计周期
        map = new HashMap<>(1);
        map.put("value", thing2Value);
        objMap.put("thing2", map);

        return objMap;
    }
}

订阅消息发送工具类

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.druid.support.json.JSONUtils;
import com.ruoyi.RemoteSysConfigService;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.domain.SysUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 发送小程序信息
 *
 * @author Administrator
 */
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Slf4j
public class AppletMsgSendUtil {

    private final RestTemplate restTemplate;

    private final StringRedisTemplate redisTemplate;


    /**
     * 发送小程序订阅信息
     * @param templateId    所需下发的订阅模板id
     * @param page          点击模板卡片后的跳转页面
     * @param data          模板内容
     * @return
     */
    public void msgSend(String templateId, String page, Map<String, Object> data) {
        try {
            
            //region 获取access_token
            String accessToken = getAppletToken();
            //endregion

            //region 推送小程序信息
            String msgUrl = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken;
            // 参数处理
            Map<String, Object> params = new HashMap<>(6);
            params.put("touser", openId);
            params.put("template_id", templateId);
            params.put("page", page);
            params.put("lang", "zh_CN");
            params.put("miniprogram_state", "formal");
            params.put("data", data);
            String jsonData = JSONUtils.toJSONString(params);
            HttpEntity<String> request = new HttpEntity<>(jsonData);
            String msgResult = restTemplate.postForObject(msgUrl, request, String.class);
            JSONObject msgResultObject = JSONUtil.parseObj(msgResult);

            if (!"0".equals(msgResultObject.get("errcode").toString())) {
                throw new RuntimeException("发送订阅消息失败," + msgResultObject.get("errmsg"));
            }
            //endregion
        } catch (Exception e) {
            throw new RuntimeException("发送订阅消息失败," + e);
        }
        //endregion
    }

    /**
     * 获取小程序token
     * @return
     */
    public String getAppletToken () {
        // 先从缓存查看有没有
        String appletToken = redisTemplate.opsForValue().get("AppletToken");
        if (!StringUtil.isBlank(appletToken)) {
            return appletToken;
        }
        
        //设置查询参数与请求url
        MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
        queryParams.add("appid", appid);
        queryParams.add("secret", secret);
        String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
        UriComponentsBuilder tokenBuilder = UriComponentsBuilder.fromHttpUrl(tokenUrl).queryParams(queryParams);
        //获取token
        String tokenResult = restTemplate.getForObject(tokenBuilder.toUriString(), String.class);
        JSONObject tokenObject = JSONUtil.parseObj(tokenResult);
        appletToken = tokenObject.getStr("access_token");
        if (StringUtil.isBlank(appletToken)) {
            throw new RuntimeException("小程序token获取失败," + tokenObject.getStr("errmsg"));
        }
        //将token存到redis,有效期100分钟。官方接口返回的token有效期120分钟
        redisTemplate.opsForValue().set("AppletToken", appletToken);
        redisTemplate.expire("AppletToken", 100, TimeUnit.MINUTES);

        return appletToken;
    }

}

用法

// 发送消息通知领取人已领取物品
Map<String, Object> objMap = AppletParamUtil.getDataReportParam(thing1Value,thing2Value);

msgSendUtil.msgSend(模板id, 需要跳转的小程序页面, objMap);

注意:参数拼接工具类里面方法的各个参数是根据模板而定的,模板有多少个参数,工具类方法就有多少个参数
微信小程序订阅消息,微信小程序,小程序,订阅消息

最后成功发送订阅消息的效果就是这样了
微信小程序订阅消息,微信小程序,小程序,订阅消息

另外,附上apipost的订阅消息发送调用方式。参数含义看上面贴出的官方文档

url:https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=你的token
method:POST
body:
{ “touser”: “”,
“template_id”: “”,
“page”: “”,
“lang”:“zh_CN”,
“miniprogram_state”: “formal”,
“data”: {} }

微信小程序订阅消息,微信小程序,小程序,订阅消息

有什么不懂的去微信开放社区问,去发帖。https://developers.weixin.qq.com/community/develop/mixflow

参考:
(1)https://developers.weixin.qq.com/community/develop/doc/0008aa1fd40ec80b4710e9b2260000
(2)https://developers.weixin.qq.com/community/develop/doc/000ce8a9298950338310bc75966800文章来源地址https://www.toymoban.com/news/detail-715988.html

到了这里,关于手把手教你实现微信小程序向特定用户推送一次性订阅消息的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 手把手教你绘制小程序海报

    海报分享功能在许多应用中应该是很常见的,因为它作为一种常用的应用推广和拉新的方式。 接下来看个实际的案例,如下: 把任务拆解下: 如何绘制海报 如何把绘制后的海报保存到相册 用 canvas 来绘制海报。 这里需要了解基本的 canvas api ,不熟悉可以先去了解下相关

    2024年02月04日
    浏览(39)
  • 手把手教你小程序反编译

    1.反编译工具unveilr :百度网盘链接:https://pan.baidu.com/s/10Wle8CwvBq54GPWcbEnxLQ 提取码:bivh   解压即可用。 2.微信开发者工具:https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html 1.获取小程序存储文件夹 (1)打开PC端微信设置,在文件管理中找到存储路径,选择打开文件夹。

    2024年04月12日
    浏览(32)
  • 手把手教你开通小程序流量主

    开通条件是累计独立访客不低于 1000。也就是1000级以上,其实这个不难。 接下来以防火安全知识专项学习与竞答为例,写一篇开通流量主、创建广告和代码嵌入的图文教程。 广告展示位置灵活控制,接入简单,仅需复制广告插件嵌入代码 数据精准透明,可按天查看收入 广告

    2024年02月09日
    浏览(30)
  • 【抓包教程】微信小程序精准流量抓包教程(超详细 保姆级教程 BP安装证书手把手教)

    1.打开浏览器,右上角打开设置。搜索代理 我们点击最后一个,打开计算机的代理设置。自动跳转到设置界面。 按照如下图配置 这里的地址和端口要和我们BP中的一致,我们需要打开bp看看。 这里配置相同后,我们打开浏览器,输入以下内容,下载证书 点击右上角,会自动下

    2024年02月21日
    浏览(34)
  • [Linux]手把手教你制作进度条小程序

    C语言中字符分为两种: 可显字符 控制字符 其中可显字符就是字符a这类的字符,控制字符就是n这种控制字符。 对于我们制作进度条,我们只需要关注两个控制字符: r – 进行回车操作 n – 进行换行加回车操作 说明: n本身是换行字符,但是C语言本身将其解析成了换行加回

    2024年02月14日
    浏览(36)
  • 手把手教你写出第一个C语言程序

    大家好,我是努力学习游泳的鱼。这篇文章将手把手带你写出人生中第一个C语言程序, Hello, World 。在阅读本文之前,建议先百度 visual studio ,在微软官网下载并安装VS的最新版本,安装时记得勾选“C++桌面开发”选项。 2.1 环境 工欲善其事必先利其器,我们需要写C语言代码

    2024年02月10日
    浏览(37)
  • 手把手教你实现SpringBoot的监控!

    任何一个服务如果没有监控,那就是两眼一抹黑,无法知道当前服务的运行情况,也就无法对可能出现的异常状况进行很好的处理,所以对任意一个服务来说,监控都是必不可少的。 就目前而言,大部分微服务应用都是基于 SpringBoot 来构建,所以了解 SpringBoot 的监控特性是非

    2024年02月11日
    浏览(36)
  • 手把手教你在ARM板上写一个驱动程序!

    摘要:搞嵌入式有两个方向,一个是嵌入式软件开发(MCU方向),另一个是嵌入式软件开发(Linux方向)。其中MCU方向基本是裸机开发和RTOS开发。而Linux开发方向又分为驱动开发和应用开发。其中应用开发相比于驱动开发来说简单一些,因为搞驱动你要和Linux内核打交道。而我们普

    2024年02月01日
    浏览(72)
  • 深入浅出:手把手教你实现单链表

    链表是一种 链状数据结构 。简单来说,要存储的数据在内存中分别独立存放,它们之间通过某种方式相互关联。 如果我们使用C语言来实现链表,需要声明一个 结构体 作为链表的结点,结点之间使用指针关联。 单向链表的每个结点内都有一个指针指向下一个结点,从而把所

    2024年02月10日
    浏览(34)
  • 手把手教你快速实现内网穿透

    前言 要想实现在公网访问到本地的项目,除了将其部署到云服务器上外,还可以通过内网穿透来实现,避免了项目出现问题修改后需要重新部署到云服务器上比较繁琐的步骤,也不需要公网IP以及服务器。 内网穿透,简单来说就是通过公网IP服务器进行数据流量转发,将原本

    2024年02月13日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包