小程序云开发一次性订阅(记录次数)功能实现
项目源码链接:https://pan.baidu.com/s/1SxwMPoA6xgUwqn3O_Wpvrg
提取码:6630
产品展示
1.问题需求分析
需求一:由于小程序一次性订阅没有记录次数功能,所以需要开发者自己记录订阅成功的次数。
需求二:同一个模板订阅消息指定对象类型发布
2.前期准备
搜索微信公众平台进入小程序后台开启订阅服务申请选择模板获取templateId(小程序后台没有的注册一个)
打开微信云开发(将数据库权限设置任何人可读可写)
3.设计数据库
这里需要个集合一个是number(记录每个用户类型的订阅成功的次数)和subscribe(发布订阅的内容分集合)字段如图
4.设计云函数
获取openId的云函数getOpenId
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
发布订阅的云函数sendSubscribeMessage,这里的data数据格式要遵循以下格式
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
})
exports.main = async (event, context) => {
try {
const result = await cloud.openapi.subscribeMessage.send({
"touser": event.openid,
"page": 'pages/see/see?message='+event.message,
"lang": 'zh_CN',
"data": {
"thing4": {
"value": event.source
},
"name3": {
"value": event.name
},
"thing1": {
"value": event.content
},
},
"templateId": 'UhlXQ253j8Wc3miwFz2ns_wI50RlSWS3VTiG612ocj0',
"miniprogramState": 'developer'
})
return result.errCode
} catch (err) {
return err
}
}
5.小程序代码逻辑设计
index(订阅页面)的js文件(主要是SubscribeMessage方法)
// index.js
// 获取应用实例
const app = getApp()
const DB_number = wx.cloud.database().collection("number")
Page({
data: {
list: [{
name: '南昌大学',
school: 0,
tmplId: 'UhlXQ253j8Wc3miwFz2ns_wI50RlSWS3VTiG612ocj0'
},
{
name: '江西师范大学',
school: 1,
tmplId: 'UhlXQ253j8Wc3miwFz2ns_wI50RlSWS3VTiG612ocj0'
},
{
name: '南昌航空大学',
school: 2,
tmplId: 'UhlXQ253j8Wc3miwFz2ns_wI50RlSWS3VTiG612ocj0'
},
{
name: '江西财经大学',
school: 3,
tmplId: 'UhlXQ253j8Wc3miwFz2ns_wI50RlSWS3VTiG612ocj0'
},
],
openid: ''
},
async onLoad() {
let that = this
let res = await wx.cloud.callFunction({
name: 'getOpenId'
})
that.setData({
openid: res.result.openid
})
DB_number.where({
openid: res.result.openid
}).get({
success(reply) {
// console.log(reply)
let list0 = that.data.list.map((item, index) => {
item.num = 0
reply.data.forEach((item2, index2) => {
if (item2.school === item.school) {
item.num = item2.num
}
})
return item
})
that.setData({
list: list0
})
}
})
},
getUserProfile(e) {
// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
wx.getUserProfile({
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
console.log(res)
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
},
getUserInfo(e) {
// 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
console.log(e)
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
},
getOpenId() {
wx.cloud.callFunction({
name: 'getOpenId'
}).then(res => {
console.log(res.result.openid)
}).catch(err => {
console.log(err)
})
},
async SubscribeMessage(e) {
let that = this
let school = parseInt(e.target.dataset.school)
// console.log(res.result.openid)
let openid = that.data.openid
wx.requestSubscribeMessage({
tmplIds: [e.target.dataset.tmplid],
success(res0) {
console.log(res0)
if (res0[e.target.dataset.tmplid] === "accept") {
let listNew = that.data.list.map((item, index) => {
if (item.school === school) {
item.num++
}
return item
})
that.setData({
list: listNew
})
DB_number.where({
openid: openid,
school: school,
}).get({
success(reply) {
//判断数据库是否有这个用户的id
// console.log(reply)
if (reply.data.length > 0) {
// 修改数据
// console.log(reply.data[0]._id, "和", reply.data[0].num)
let num = reply.data[0].num + 1
DB_number.doc(reply.data[0]._id).update({
data: {
num: num
},
success(res) {
console.log(res, "修改成功")
},
fail(err) {
console.log(err, "修改失败")
}
})
} else {
// 增加一条新的数据
DB_number.add({
data: {
openid: openid,
school: school,
num: 1
}
})
}
}
})
}else{
let listNew = that.data.list.map((item, index) => {
item.num=0
return item
})
that.setData({
list: listNew
})
DB_number.where({
openid: openid
}).get({
success(reply) {
//判断数据库是否有这个用户的id
// console.log(reply)
if (reply.data.length > 0) {
// 修改数据
// console.log(reply.data[0]._id, "和", reply.data[0].num)
// let num = reply.data[0].num + 1
reply.data.forEach((item,index)=>{
DB_number.doc(item._id).update({
data: {
num: 0
},
success(res) {
// console.log(res, "修改成功")
},
fail(err) {
// console.log(err, "修改失败")
}
})
})
} else {
// 增加一条新的数据
DB_number.add({
data: {
openid: openid,
school: school,
num: 0
}
})
}
}
})
}
},
fail(err) {
console.log(err)
}
})
},
tologin() {
wx.navigateTo({
url: '/pages/login/login'
})
},
/**
* 生命周期函数--监听页面显示
*/
async onShow() {
let that = this
let res = await wx.cloud.callFunction({
name: 'getOpenId'
})
that.setData({
openid: res.result.openid
})
DB_number.where({
openid: res.result.openid
}).get({
success(reply) {
// console.log(reply)
let list0 = that.data.list.map((item, index) => {
item.num = 0
reply.data.forEach((item2, index2) => {
if (item2.school === item.school) {
item.num = item2.num
}
})
return item
})
that.setData({
list: list0
})
}
})
},
})
index的wxml代码
<!--index.wxml-->
<view style="padding: 0 30rpx;">
<view style="display: flex;justify-content: space-between;align-items: center;margin: 50rpx 0;">
<view>
<view style="font-weight: 600;">开通订阅消息功能</view>
<view style="font-size: 26rpx;color: #7a7a7a;padding: 5rpx 0;">向你发送小程序订阅消息</view>
</view>
<switch checked/>
</view>
<view style="display: flex;justify-content: space-between;align-items: center;margin: 50rpx 0;">
<view>
<view style="font-weight: 600;">订阅消息说明</view>
<view style="font-size: 26rpx;color: #7a7a7a;padding: 5rpx 0;">当订阅次数为0时,无法收到对应的消息</view>
<view style="font-size: 26rpx;color: #7a7a7a;">当订阅次数小于5时,以红色提醒</view>
<view style="font-size: 26rpx;color: #7a7a7a;">当取消订阅时,所有订阅次数都会重置为零</view>
</view>
</view>
<view style="display: flex;justify-content: space-between;align-items: center;padding: 30rpx 0;border-bottom: #7a7a7a 1rpx dotted;"
wx:for="{{list}}"
wx:key="{{item.school}}"
>
<view>
<view style="font-weight: 600;">{{item.name}}</view>
<view style="font-size: 26rpx;color: #7a7a7a;padding: 10rpx 0;" wx:if="{{item.num>=5}}">共订阅{{item.num}}次</view>
<view style="font-size: 26rpx;color: #ff0000;padding: 10rpx 0;" wx:else>共订阅{{item.num}}次</view>
</view>
<button style="width: 150rpx;background-color: #07c160;color: #fff;font-size: 26rpx;margin: 0;padding: 18rpx;"
bindtap="SubscribeMessage"
data-tmplId="{{item.tmplId}}"
data-school="{{item.school}}"
>订阅+1</button>
</view>
<view style="position: fixed;bottom: 0;left: 0;width: 100%;text-align: center;margin: 50rpx 0;font-size: 40rpx;">or我是管理员,选择
<text style="color: #006eff;" bindtap="tologin">登入</text>
</view>
<!-- <button bindtap="getOpenId">获取用户id</button>
<button bindtap="SubscribeMessage">订阅消息</button> -->
<!-- <button bindtap="sendSubscribeMessage">发送订阅</button> -->
</view>
index的wxss代码
/**index.wxss**/
.userinfo {
display: flex;
flex-direction: column;
align-items: center;
color: #aaa;
}
.userinfo-avatar {
overflow: hidden;
width: 128rpx;
height: 128rpx;
margin: 20rpx;
border-radius: 50%;
}
.usermotto {
margin-top: 200px;
}
发布页面send.js
// pages/send/send.js
const DB_number = wx.cloud.database().collection("number")
const DB_subscribe = wx.cloud.database().collection("subscribe")
const _ = wx.cloud.database().command
let timer0 = null
let timer1 = null
let timer2 = null
let timer3 = null
Page({
/**
* 页面的初始数据
*/
data: {
source: '微博',
name: 'kkzz',
rank: '1级',
content: '生而为人,我很抱歉',
school: [{
type: 0,
name: '南昌大学'
},
{
type: 1,
name: '江西师范大学'
},
{
type: 2,
name: '南昌航空大学'
},
{
type: 3,
name: '江西财经大学'
},
],
type: 0,
username: ''
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.setData({
username: options.username
})
},
bindPickerChange(e) {
this.setData({
type: e.detail.value
})
},
inputsource(e) {
let that = this
if (timer0) {
clearTimeout(timer0)
}
timer0 = setTimeout(() => {
that.setData({
source: e.detail.value
})
}, 200)
},
inputname(e) {
let that = this
if (timer1) {
clearTimeout(timer1)
}
timer1 = setTimeout(() => {
that.setData({
name: e.detail.value
})
}, 200)
},
inputrank(e) {
let that = this
if (timer2) {
clearTimeout(timer2)
}
timer2 = setTimeout(() => {
that.setData({
rank: e.detail.value
})
}, 200)
},
inputcontent(e) {
let that = this
if (timer3) {
clearTimeout(timer3)
}
timer3 = setTimeout(() => {
that.setData({
content: e.detail.value
})
}, 200)
},
async sendSubscribeMessage() {
let that = this
wx.showModal({
title: '提示',
content: '你确定要发布吗',
success(res) {
if (res.confirm) {
// console.log('用户点击确定')
let list_openid
DB_number.where({
school: parseInt(that.data.type),
num: _.gt(0)
}).get({
success(reply) {
console.log(reply.data)
DB_subscribe.add({
data: {
username: that.data.username,
school: parseInt(that.data.type),
name: that.data.name,
rank: that.data.rank,
source: that.data.source,
content: that.data.content,
type: parseInt(that.data.type),
time: new Date()
},
success(res0){
list_openid = reply.data
list_openid.forEach((item, index) => {
wx.cloud.callFunction({
name: 'sendSubscribeMessage',
data: {
openid: item.openid,
name: that.data.name,
rank: that.data.rank,
source: that.data.source,
content: that.data.content,
message:res0._id
}
}).then(res => {
if(item.num!==0){
DB_number.doc(item._id).update({data:{
num:item.num-1
}})
}
if (index === 0) {
wx.showToast({
title: '发布成功',
icon: 'success'
})
}
console.log(res)
}).catch(err => {
console.log(err)
wx.showToast({
title: '发布失败',
icon: 'error'
})
})
})
}
})
}
})
} else if (res.cancel) {
// console.log('用户点击取消')
}
}
})
},
before(){
wx.navigateTo({
url: '/pages/detailed/detailed?username='+this.data.username,
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
发布页面send.wxml
<!--pages/send/send.wxml-->
<view style="padding: 0 30rpx;">
<view>
<view style="padding: 30rpx 0;font-weight: 600;">用户来源:</view>
<input style="border: rgb(194, 192, 192) 1rpx solid;height: 80rpx;padding: 0 20rpx;" placeholder="例:微博"
bindinput="inputsource" />
</view>
<view>
<view style="padding: 30rpx 0;font-weight: 600;">用户名称:</view>
<input style="border: rgb(194, 192, 192) 1rpx solid;height: 80rpx;padding: 0 20rpx;" placeholder="例:kkzz"
bindinput="inputname"
/>
</view>
<view>
<view style="padding: 30rpx 0;font-weight: 600;">预警级别:</view>
<input style="border: rgb(194, 192, 192) 1rpx solid;height: 80rpx;padding: 0 20rpx;" placeholder="例:1级"
bindinput="inputrank"
/>
</view>
<picker mode="selector" value="{{type}}" range="{{school}}" bindchange="bindPickerChange" range-key="name">
<view>
<view style="padding: 30rpx 0;font-weight: 600;">发布对象:</view>
<view style="border: rgb(194, 192, 192) 1rpx solid;text-align: center;line-height: 80rpx;height: 80rpx;padding: 0 20rpx;">{{school[type].name}}</view>
</view>
</picker>
<view>
<view style="padding: 30rpx 0;font-weight: 600;">内容:</view>
<textarea style="border: rgb(194, 192, 192) 1rpx solid;padding: 0 20rpx;width: 640rpx;" placeholder="例:生而为人,我很抱歉"
bindinput="inputcontent"
/>
</view>
<view style="color: blue;text-align: right;margin-top: 30rpx;" bindtap="before">以往发布>></view>
<button style="position: fixed;left: 30rpx;bottom: 0;width: 690rpx;margin: 50rpx 0;background-color: rgb(77, 77, 228);color: #fff; border-radius: 50rpx;"
bindtap="sendSubscribeMessage"
>发布订阅</button>
</view>