微信小程序的订阅消息是小程序的重要能力之一,为实现服务的闭环提供更优的体验。订阅消息我们应该经常见到,比如下单成功之后的服务通知,支付成功后的支付成功通知,都属于小程序的订阅消息。

本文只实现一次性订阅的功能,至于长期订阅设备订阅,有机会碰到再进行研究。

在开始之前,我们先看看微信小程序订阅消息的介绍:

功能介绍

消息能力是小程序能力中的重要组成,我们为开发者提供了订阅消息能力,以便实现服务的闭环和更优的体验。

  • 订阅消息推送位置:服务通知
  • 订阅消息下发条件:用户自主订阅
  • 订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面

消息类型

1. 一次性订阅消息

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

2. 长期订阅消息

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

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

所以我们普通小程序,在注册成功后,订阅消息的模板选择,只有一次性订阅的选项,没有长期订阅的选项。

3. 设备订阅消息

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

了解了小程序订阅消息之后,我们开始进入正题!

基本流程

注意事项

由于后面的文章还很长,注意事项优先发出来,可能看到这里已经解决了你的问题。

  • 一次性模板 id 和永久模板 id 不可同时使用。
  • 低版本基础库2.4.4~2.8.3 已支持订阅消息接口调用,仅支持传入一个一次性 tmplId / 永久 tmplId。
  • 2.8.2 版本开始,用户发生点击行为或者发起支付回调后才可以调起订阅消息界面
  • 2.10.0 版本开始,开发版和体验版小程序将禁止使用模板消息 formId。
  • 一次授权调用里,每个 tmplId 对应的模板标题不能存在相同的,若出现相同的,只保留一个。
  • 2.10.0 版本开始,支持订阅语音消息提醒

特别注意第三条,版本库是2.8.2及以上的时候,订阅消息必须发生点击行为或是发起支付回调后,才可以调起订阅消息的界面。这个点击行为没有特别要求。比如一个表单,点击提交按钮后,也是可以调起订阅消息界面的。支付后的回调不需要点击行为,也可以调起订阅消息界面。

获取模板ID

在微信公众平台登录小程序,在订阅消息功能下,进入到我的模板,找到模板,并将模板id复制出来,如果没有模板,需要先添加模板,再获取模板id

要添加新模板,点击选用按钮,在公共模板库中选择需要的模板,添加就可以了。

有很多文章说,如果没有合适的模板,可以创建自定义模板。但如果你真想去创建自定义模板,会发现根本找不到地方。

如果想创建自定义模板,可通过以下方式进行。

1、点击选用按钮,来到公共模板库。(公共模板库中的模板,与你小程序的服务类目相关)

2、在搜索框中,输入比较长的关键词。

3、点击搜素,如果还是能匹配出模板来,则重新调整关键词,直到没有任何搜索结果为止。

4、点击页面中的帮忙我们完善模板库,进行自定义模板设置。



创建自定义模板的时候,一定要仔细阅读申请模板的流程,尤其是第1条。我单拉出来重点标注一下,因为没仔细看第1条,第一次申请的几个模板白白等了好几天。

模板标题需体现具体的服务场景,要求以“通知”或“提醒”结尾,如:物流到货通知、交易提醒。

看到这里,会发现以上大部分跟网上的文章没啥区别,别急,正文来了!

uni-app代码

前端实现的是点击提交按钮,保存表单,保存成功后发送订阅消息,在pages/index/index.vue下编写如下代码:

<template>
<view>
<view class="setp">
<publishStep :list="setpList" :current="0" mode="number" active-color="#eb3572"></publishStep>
</view>
<view class="container">
<u-form :model="form" ref="uForm" :rules="rules" :error-type="errorType">
<u-form-item label="姓名" label-width="160rpx" :border-bottom="true" :label-style="{'font-size':'28rpx'}" prop="realName">
<u-input v-model="form.realName" placeholder="" input-align="right" />
</u-form-item> <u-form-item label="服务时间" label-width="160rpx" :border-bottom="true" :label-style="{'font-size':'28rpx'}"
right-icon="arrow-right" prop="serviceTime">
<u-input v-model="form.serviceTime" placeholder="请选择服务时间" :disabled="true" input-align="right" @click="timeShow=true" />
</u-form-item> <u-form-item label="服务地址" label-width="160rpx" :border-bottom="true" :label-style="{'font-size':'28rpx'}" prop="serviceAddress">
<u-input v-model="form.serviceAddress" placeholder="" input-align="right" @click="selectAddress" />
</u-form-item>
<u-form-item label="联系电话" label-width="160rpx" :border-bottom="true" :label-style="{'font-size':'28rpx'}"
prop="lxtel">
<u-input v-model="form.lxtel" type="number" placeholder="请输入联系电话" input-align="right" :clearable="false" />
</u-form-item>
<u-form-item label="需求描述" label-width="160rpx" :border-bottom="true" :label-style="{'font-size':'28rpx'}"
prop="remarks">
<u-input v-model="form.remarks" type="text" placeholder="请输入您的需求" input-align="right" :clearable="false" />
</u-form-item>
</u-form>
</view>
<view style="height: 160rpx;"></view>
<view class="bottom_nav">
<view class="buttom_box padding-horizontal-20 padding-vertical-10">
<u-button type="error" @click="submitForm" :loading="submit_loading" style="height: 100rpx; font-weight: bold; font-size: 36rpx;">确认提交</u-button>
</view>
</view>
<u-picker mode="time" v-model="timeShow" :params="timeParams" @confirm="timeConfirm"></u-picker>
</view>
</template>
<script>
export default {
data() {
return { form:{
realName:"",
serviceTime:'',
serviceAddress:"",
lxtel:"",
remarks:"" },
rules:{
realName: [{
required: true,
message: "请填写您的姓名",
trigger: 'change'
}], serviceTime: [{
required: true,
message: "请选择服务时间",
trigger: 'change'
}], lxtel: [{
required: true,
message: "请输入联系电话",
trigger: 'change'
}],
},
errorType: ['toast'], timeShow:false,
timeParams:{
year: true,
month: true,
day: true,
hour: false,
minute: false,
second: false
},
submit_loading:false,
}
}, onReady() {
this.$refs.uForm.setRules(this.rules);
}, onLoad(params) {
let that = this;
},
methods: { timeConfirm(e){
let that = this;
that.form.serviceTime = e.year +"-"+e.month+"-"+e.day
},
gotoOrder(){
uni.redirectTo({
url:"/pages/order/order"
})
},
submitForm(){
let that = this;
this.$refs.uForm.validate(valid=>{
if (valid){
that.$u.api.submit_order(that.form).then(res => {
if (res.success) {
let data = res.data;
uni.showToast({
title: '提交成功',
icon: 'success'
})
// #ifdef MP-WEIXIN
uni.requestSubscribeMessage({
tmplIds:['XXXXXXXXXXX'], //这里填写tempid
success:function(subscribeMessageRes){
if(subscribeMessageRes.errMsg=="requestSubscribeMessage:ok"){
if(subscribeMessageRes.XXXXXXXXXXX=="accept"){
uni.login({
provider: 'weixin',
success:function(loginRes){
if(loginRes.errMsg=="login:ok"){
const code = loginRes.code;
that.$u.api.sendSubscribeMessage({
"code":code,
"orderId":data.orderId
}).then(res=>{
that.gotoOrder()
})
}else{
that.gotoOrder()
}
},
fail() {
that.gotoOrder()
}
})
}else{
that.gotoOrder()
}
}else{
that.gotoOrder()
} },
fail:function(){
that.gotoOrder()
}
})
// #endif } else {
uni.$u.toast(res.message);
}
});
}
}) }
}
}
</script> <style>
.setp{ padding: 40rpx 0;}
.bottom_nav {
position: fixed;
width: 100%;
height: 100rpx;
left: 0;
bottom: 0;
z-index: 9999;
background: #FFFFFF;
border-top: 1rpx #f3f3f3 solid;
}
</style>

这里的流程分为3步:

1、提交表单,服务端返回订单号(orderId)

2、使用uni.requestSubscribeMessage,调起授权框,当点击同意后,进入第三步。调起授权后,如果用户同意,回调函数的参数subscribeMessageRes有两个对象:errMsgXXXXXXXXXXX,errMsg不必多说。主要是这个XXXXXXXXXXX是什么。XXXXXXXXXXX是授权生成的,目测来看就是模板Id。

3、使用uni.login,获取code

4、将codeorderId发送到服务器,服务器通过code获取到openId,再根据orderId获取到具体订单数据。

5、发送模板消息。

如果不出意外的话,提交成功后,弹出如下授权框

服务端代码

服务端ORM使用SqlSugar,微信小程序接口使用SKIT.FlurlHttpClient.Wechat库。

生成订单

提交订单,这里只做演示,具体的代码自己实现下就可以了!

[HttpPost]
public async Task<AjaxResult> SubmitOrder(order model)
{
//生成订单号
model.order_no = DateTime.Now.ToString("yyyyMMddHHssfffff");
model.addtime = DateTime.Now;
//ExecuteReturnIdentity方法会返回自增id
var id = await db.Insertable(model).ExecuteReturnIdentity();
return new AjaxResult(){
success=true,
data = id
};
}

AjaxResult.cs

public class AjaxResult
{
/// <summary>
/// 是否成功
/// </summary>
public bool success { get; set; } = true; /// <summary>
/// 错误代码
/// </summary>
public int code { get; set; } = 0; /// <summary>
/// 返回消息
/// </summary>
public string message { get; set; }
/// <summary>
/// 返回数据
/// </summary>
public object data{ get; set;} }

order.cs

[SugarTable("order")]
public class order
{
/// <summary>
/// 主键,自增Id
/// </summary>
[SugarColumn(IsPrimaryKey = true)]
public int id { get; set; }
/// <summary>
/// 订单编号
/// </summary>
public string order_no { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string realName { get; set; }
/// <summary>
/// 时间
/// </summary>
public DateTime serviceTime { get; set; }
/// <summary>
/// 地址
/// </summary>
public string serviceAddress { get; set; }
/// <summary>
/// 联系电话
/// </summary>
public string lxtel { get; set; }
/// <summary>
/// 备注
/// </summary>
public string remarks { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime addtime { get; set; }
}

发送模板消息

发送一次性订阅的模板消息,传的参数为前端获取的codeorderId。根据订单编号获取订单信息,以便在订阅消息中,设置小程序信息以及打开路径。code用于获取用户的openId

[HttpPost]
public async Task<AjaxResult> SendSubscribeMessage(string code,string orderId)
{
AjaxResult result = new AjaxResult();
if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(orderId))
{
result.success = false;
result.message = "参数错误";
return result;
}
var order_model = await db.Queryable<order>().InSingleAsync(orderId);
if(order_model is null)
{
result.success = false;
result.message = "参数错误";
return result;
}
//初始化WechatApiClient
var options = new WechatApiClientOptions()
{
AppId = "appId",
AppSecret = "appSecret "
};
var client = new WechatApiClient(options);
//获取openId
var request = new SnsJsCode2SessionRequest();
request.JsCode = code;
var response = await client.ExecuteSnsJsCode2SessionAsync(request);
string openId = response.OpenId;
//获取token
var tokenRequest = new CgibinTokenRequest();
var tokenResponse = await client.ExecuteCgibinTokenAsync(tokenRequest);
var token = tokenResponse.AccessToken;
//发送模板消息
var messageRequest = new CgibinMessageSubscribeSendRequest();
IDictionary<string, CgibinMessageSubscribeSendRequest.Types.DataItem> messageData = new Dictionary<string, CgibinMessageSubscribeSendRequest.Types.DataItem>
{
{
"params1",
new CgibinMessageSubscribeSendRequest.Types.DataItem() {Value=order_model.order_no}
},
{
"params1",
new CgibinMessageSubscribeSendRequest.Types.DataItem(){Value=order_model.userNmae}
},
{
"params3",
new CgibinMessageSubscribeSendRequest.Types.DataItem(){Value=order_model.serviceTime}
},
{
"params4",
new CgibinMessageSubscribeSendRequest.Types.DataItem(){Value=order_model.serviceAddress}
},
{
"params5",
new CgibinMessageSubscribeSendRequest.Types.DataItem(){Value=order_model.addtime.ToString("yyyy-MM-dd HH:ss")}
}
};
messageRequest.AccessToken = token;
messageRequest.ToUserOpenId = openId;
messageRequest.TemplateId = "XXXXXXXXXXX";
messageRequest.MiniProgramState = "developer";
//微信小程序要跳转的地址。可以加参数
messageRequest.MiniProgramPagePath = "/pages/order/order_details?id=" + order_model.id;
messageRequest.Data = messageData;
var messageResponse = await client.ExecuteCgibinMessageSubscribeSendAsync(messageRequest);
if(messageResponse.ErrorCode==0)
{
result.success=true;
result.message = "ok";
return result;
}
result.success = false;
result.message = "error";
return result;
}

构造模板消息的时候,使用IDictionary<string, CgibinMessageSubscribeSendRequest.Types.DataItem> messageData = new Dictionary<string, CgibinMessageSubscribeSendRequest.Types.DataItem>来进行构造,

假设一个模板消息的详细内容是这样的:

  • 那么上面代码中的params1 就是character_string22,同理params2就是thing7。也就是说。IDictionary的key就是模板中.DATA前面的内容。
  • messageRequest.TemplateId,要与前端的模板Id一致。
  • messageRequest.MiniProgramState表示跳转微信小程序的类型。默认为正式版
    • developer为开发版;
    • trial为体验版;
    • formal为正式版;

如果不出意外的话,你的微信会收到服务通知。点击卡片后,进入小程序的订单详情页面!

总结

1、其实微信小程序的订阅消息和公众号的订阅消息模板还是比较好申请的。如果在类目模板与历史模板中无法找到合适自己的模板,那么自己申请一个模板。审核的话,2-3天就可以收到通知了。

需要注意的是,申请模板的时候,最好把各项在本地保留一份。因为一旦提交申请,在公众号或小程序后台,你就找不到了。玩意审核没通过,再申请的时候,前面写的啥内容,已经忘的差不多了!

2、感谢SqlSugar,为.Net开发者提供这么强大的ORM。真的是太方便了。

3、感谢SKIT.FlurlHttpClient.Wechat,为.Net开发者提供这么便捷的工具。

4、为了能快速表达清楚意思,以上前端与服务端代码,都是精简过的,万万不可直接使用!


作者:gmval


出处:https://www.cnblogs.com/gmval/p/17071237.html


本文版权归作者和博客园共有,写文不易,支持原创,欢迎转载【点赞】,转载请保留此段声明,且在文章页面明显位置给出原文连接,谢谢。

关注个人公众号,定时同步更新优秀资源及技术文章

uni-app + .NET 7实现微信小程序订阅消息推送的更多相关文章

  1. node.js微信小程序配置消息推送

    在开发微信小程序时,有一个消息推送,它的解释是这样的. 消息推送具体的内容是下面的这个网址   https://developers.weixin.qq.com/miniprogram/dev/fra ...

  2. 微信小程序【消息推送服务器认证C# WebAPI】

    参考微信开发文档: https://developers.weixin.qq.com/miniprogram/dev/api/custommsg/callback_help.html 代码可用 /// ...

  3. 微信小程序订阅消息

    概述 消息能力是小程序能力中的重要组成,我们为开发者提供了订阅消息能力,以便实现服务的闭环和更优的体验. 订阅消息推送位置:服务通知 订阅消息下发条件:用户自主订阅 订阅消息卡片跳转能力:点击查看详情 ...

  4. 微信小程序简单的推送消息流程

    1.进入开发设置-消息推送,启用消息推送 url: 启用并设置消息推送配置后,用户发给小程序的消息以及开发者需要的事件推送,都将被微信转发至该服务器地址中. 2.创建消息模板. 3.WXML代码: 4 ...

  5. 微信小程序订阅消息,我踩过的坑都在这里了!

    旧的模板消息将在 2020 年 1 月 10 号全面下架,也就是今天,不过貌似现在还可以用!!!我已经改好了,只不过还没有上线,准备坚持到最后一天! 0.订阅消息 简单介绍一下订阅消息的特点: 用户授 ...

  6. 微信小程序订阅消息调研

    相关资料 背景:微信模板消息已正式下架,改为订阅消息,详情如下: 服务变更通知 订阅消息:订阅消息相关内容如下: 订阅消息 接口设计 获取接口访问凭证 :根据appId和secret获取接口访问凭证a ...

  7. errcode 4103 invalid page hint 小程序模板消息推送遇到的坑

    invalid page hint一直提示这个坑爹的就是,我的小程序没发布之前,也就是测试版本用这个格式是可以的 /pages/myGroup/myGroup?groupid=22***但是发布成功以 ...

  8. node配置微信小程序解密消息以及推送消息

    上一篇文章介绍过 微信小程序配置消息推送,没有看过的可以先去查看一下,这里就直接去把那个客服消息接口去解密那个消息了. 在这里我选择的还是json格式的加密. 也就是给小程序客服消息发送的消息都会被微 ...

  9. 微信小程序订阅

    微信小程序订阅 摘要 1.基于promise封装微信小程序订阅 2.解决由于微信基础库版本低下的兼容 3.解决“总是保持以上选择,不再询问”的取消状态 主要运用API: requestSubscrib ...

  10. 微信小程序模板消息群发解决思路

    基于微信的通知渠道,微信为开发者提供了可以高效触达用户的模板消息能力,以便实现服务的闭环并提供更佳的体验.(微信6.5.2及以上版本支持模板功能.低于该版本将无法收到模板消息.) 模板推送位置:服务通 ...

随机推荐

  1. 抓包整理————ip 协议一[十二]

    前言 简单介绍一下ip协议. 正文 先来看下ip协议在网络层的哪一层: 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 ip 层就在网络层: 其实很好想象哈,就是因为每台机器起码有一个ip ...

  2. SpringBoot+hutool工具-数据库数据导出Excel

    hutool文档地址:https://www.hutool.cn/docs/#/ 导入依赖 <dependency> <groupId>cn.hutool</groupI ...

  3. 我的Vue之旅 11 Vuex 实现购物车

    Vue CartView.vue script 数组的filter函数需要return显式返回布尔值,该方法得到一个新数组. 使用Vuex store的modules方式,注意读取状态的方式 this ...

  4. Multipass,本地轻量级Linux体验!

    Multipass介绍 Multipass 是由Ubuntu官方提供,在Linux,MacOS和Windows上快速生成 Ubuntu虚拟机 的工具.它提供了一个简单但功能强大的CLI,可让我们在本地 ...

  5. c++ trivial, standard layout和POD类型解析

    目录 1. trivial类型 2. standard layout类型 3. 集大成者,POD(Plain Old Data)类型 4. 测试代码 1. trivial类型 占用一片连续的内存,编译 ...

  6. Burp Suite

    Burp Suite proxy代理 1.首先在浏览器中设置代理配置 火狐浏览器先点击右上角三个杠--选项--常规--网络设置 2.打开Burp Suite进行抓包 Proxy代理--options中 ...

  7. .NET周报【12月第2期 2022-12-15】

    国内文章 九哥聊Kestrel网络编程第一章:开发一个Redis服务器 https://mp.weixin.qq.com/s/HJYnBE-7wbvkAYHxQaq3eQ 我和拥有多个.NET 千星开 ...

  8. adb环境配置及常用命令

    一.adb环境配置 1.下载并安装adb驱动 2.下载adb工具platform-tools.rar,解压放在某个文件夹下 3.右击此电脑->属性->高级系统设置->环境变量-> ...

  9. RuntimeError: setuptools >= 41 required to build

    使用命令python setup.py install 安装第三方库报RuntimeError: setuptools >= 41 required to build 原因setuptools版 ...

  10. 详解Python当中的pip常用命令

    原文链接:https://mp.weixin.qq.com/s/GyUKj_7mOL_5bxUAJ5psBw 安装 在Python 3.4版本之后以及Python 2.7.9版本之后,官网的安装包当中 ...