微信小程序实现订阅消息功能(Node服务器篇)

         在上一篇内容当中在微信小程序中实现订阅消息功能,都在客户端(小程序)中来实现的,在客户端中模拟了服务器端来进行发送订阅消息的功能,那么本篇就将上一篇内容中仅在客户端中实现发送订阅消息功能进行一个分离,使用 Node.js 搭载后台服务器进行操作,客户端(小程序)通过请求提交数据信息到服务器,由服务器来发送订阅消息,如果你在没有读在客户端模拟服务器端的上一篇内容来此篇目的话可能需要有一些吃力,至少需要了解它的一个基本流程,那么本篇内容在客户端的内容不再从头开始讲起仅简单概述,重点还是在如何搭载后台以及完成客户端向服务器端进行提交数据请求处理后发送订阅消息,笔者建议小白从上一篇内容开始,再读本篇内容,可以知道在客户端模拟后端服务器和搭载后台服务器有哪些异同点,大大提高你的认识,下面进入正题。


完成效果

下面来看一下完成的效果: 


客户端页面编写

<!-- index.html -->
<!-- 顶部 -->
<view class="content">
  <view class="head">
    当前操作员:{
   
   {admin}}
  </view>
  <block wx:if="{
   
   {!hasUserInfo}}">
    <button bindtap="getUserProfile">获取</button>
  </block>
  <block wx:else>
    <form bindsubmit="formSubmit">
      <view class="log">
        <view class="log_title"> 
          <text>日志类型</text>
          <picker bindchange="changeTitle" name="title" value="{
   
   {index}}" range="{
   
   {logArray}}">
              <view class="nowing">{
   
   {logArray[index]}}</view>
          </picker>
        </view>
        <view class="log_date">
          <text>日期选择</text>
          <picker mode="date" name="date" value="{
   
   {date}}" bindchange="bindDateChange">
            <view class="nowing">{
   
   {date}}</view>   
          </picker>
        </view>
        <view class="log_person">
          <text>提醒人</text>
          <input type="text" name="remind" bindinput="bindRemindPer" bindtap ="allowSubscribeMessage" placeholder="例如:小明(参考)" value="{
   
   {remind}}"/>
        </view>
        <view class="log_tip">
          <text>温馨提示</text> 
          <input type="text" name="tip" bindinput="bindTipCont" value="{
   
   {tip}}"/>
        </view>
        <button form-type="submit" disabled="{
   
   { remind == '' ? true : false}}">发送</button>
        <view class="errMsg">{
   
   { remind == '' ? 'tips:需要填写提醒人名称才可发送!' : '' }}</view>
      </view>
    </form>
  </block>
</view>

客户端代码编写

        将前面客户端编写代码进行整理,在这里面需要填写你申请的订阅模板的id,以及form表单提交的内容待写,需要服务器端搭载编写请求接口供客户端发起请求。

// index.js
const app = getApp()
const time = require('../../utils/util')

const tempid = ''     // 填写上你自己的订阅模板id [未提交日志模板]

Page({
  data:{
    index: 0 ,                            // 默认为0,日报
    logArray:['日报','周报','月报'],
    admin:'',                             // 操作员
    lTitle:'未提交日志提醒',               // 提醒标题
    date: time.formateTime(new Date())    // 日期(格式化)
    remind:'',                            // 提醒人,默认admin              
    tip:'尚未提交日志',                    // 温馨提示内容
  },
  // 获取提醒标题
  changeTitle(e){
    this.setData({ index : e.detail.value })
  },
  // 获取日期选择
  bindDateChange(e){
    this.setData({ date : e.detail.value })
  },
  // 获取提醒人
  bindRemindPer(e){
    this.setData({ remind : e.detail.value })
  },
  // 获取提示内容
  bindTipCont(e){
    console.log(e)
    this.setData({ tip : e.detail.value })
  },
  // 获取用户信息
  getUserProfile(e) {
    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    wx.getUserProfile({
      desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
        this.setData({
          admin: res.userInfo.nickName,
          hasUserInfo: true
        })
      }
    })
  },
  // 获取用户权限 - 待写
  allowSubscribeMessage(){
    // hasSubscribeMessage 授权状态
    if (!app.globalData.hasSubscribeMessage) {
      wx.requestSubscribeMessage({
        tmplIds: [tempid], // 在此处填写模板id
        success(res) {
          console.log('获取权限:',res)
          app.globalData.hasSubscribeMessage = res.errMsg.split(':')[1]
        }
      })
    }
  },
  // 表单提交
  formSubmit(e){
    // e.detail.value 可以拿到用户提交表单的数据
    // console.log(e.detail.value)
    let formInfo = e.detail.value
    formInfo.title = this.data.logArray[formInfo.title]
    wx.login({
      timeout: 2000,
      // 成功后会返回code,将code提交给服务器
      success: res =>{
        // 获取到code
        console.log('获取到code:' + res.code)
        console.log(res)
        // 提交服务器 -- 待写
      }
    })
  }   
})

日期格式化文件

        引入 util/util.js 中存放着公共函数,有处理日期格式化函数,在index.js中来调用。

// 格式化日期
function formatTime(date) {
    var year = date.getFullYear()
    var month = date.getMonth() + 1
    var day = date.getDate()
    return [year, month, day].map(formatNumber).join('-') 
}

function formatNumber(n) {
  n = n.toString()
  return n[1] ? n : '0' + n
}

module.exports = {
  formatTime
}

下面开始进入本篇内容的一个重点部分,使用Node.js搭载后台服务器,那么对这块内容不熟悉的可以看一下这篇内容  微信小程序搭载node.js服务器(简),同时还可以到Node专栏学习一些关于Node.js的基础知识。

使用Node.js搭载后台服务器

        检查Node.js这些就不再讲了,在 微信小程序搭载node.js服务器(简) 当中已经讲过了!创建一个存放后台的文件夹,使用CMD命令切换到此目录之下,输入如下命令:

npm init -y 

        使用如下命令来安装 express 框架:

npm i express -S

         在目录下创建index.js文件作为启动文件。

         使用代码编译器 ( 这里使用的是VSCode ) 打开index.js进行代码编写如下代码:

const express = require('express')
const app = express()

// 解析表单 - 待写

app.get('/',(req,res)=>{
    res.send('ok')
})

// 监听 3000 端口号 | 127.0.0.1:3000
app.listen('3000',()=>{
    console.log('Server Running ...')
})

         测试一下,切出CMD命令窗口输入 node index.js (可安装nodemon)

         在浏览器的url地址上输入请求地址: http://127.0.0.1:3000/ ,会返回 res.send " ok "

        接下来开始编写客户端需要请求的接口。


解析表单

        body-parser是一个常用的express中间件,主要就是对 http 的请求体进行解析。否则前台发出请求所带的请求体无法被后端进行解析获取。

// 解析表单
app.use(express.json());
app.use(express.urlencoded({extended : false}))

        如果引入使用 bod-parser ,可以如下来引入编写(但没有必要,需要安装body-parser,可以使用express框架中的。)

npm i body-parser
const bodyParser = require('body-parser')

// 解析表单
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended : false}))

获取Openid

        获取Openid从上一篇内容就已然知道,获取Openid需要3个参数,分别是 code ,appid 和 appsecret ;appid 和 appsecret 可以在 微信公众平台 上登录获取,主要是 code 参数,code参数可以在客户端 (小程序) 通过 wx.login() 方法进行获取,然后发送到服务器端,再由服务器端发起请求小程序API拿到用户的Openid。

// index.js - 客户端
  ...
  // 表单提交
  formSubmit(e){
    // e.detail.value 可以拿到用户提交表单的数据
    // console.log(e.detail.value)
    let formInfo = e.detail.value
    formInfo.title = this.data.logArray[formInfo.title]
    wx.login({
      timeout: 2000,
      // 成功后会返回code,将code提交给服务器
      success: res =>{
        // 获取到code
        console.log('获取到code:' + res.code)
        console.log(res)
        // 提交服务器
        wx.request({
            url: 'http://127.0.0.1:3000/getOpenId',
            method: 'POST',
            data: {
                code: res.code
            },
            success: res=>{
                // 待写
            }
        })
      }
    })
  }  
... 

        通过 wx.request() 方法请求接口 http://127.0.0.1:3000/getOpenId ,请求参数就是 wx.login 获取到的code,下面来后台编写这个请求接口。

// index.js - 服务端
const express = require('express')
const app = express()

// 解析表单
app.use(express.json());
app.use(express.urlencoded({extended : false}))

// 用户信息
var user = {
    appid: '',    // 填写你自己的appid ( 微信公众平台获取 )
    secret: '',   // 填写你自己的appsecret密钥 ( 微信公众平台获取 )
    openid: ''
}

// 模板ID - template_id
const temp_id = ''        // 填写你申请选用的模板id

app.get('/',(req,res)=>{
    res.send('ok')
})

// 获取Openid请求接口
app.get('/getOpenId',(req,res)=>{
    // 待写
})

// 监听 3000 端口号 | 127.0.0.1:3000
app.listen('3000',()=>{
    console.log('Server Running ...')
})
...
// 获取Openid请求接口
app.get('/getOpenId',(req,res)=>{
    // console.log(req.body) 含有code参数
    console.log("客户端发来:",req.query.code)
    let data = req.query.code
    // 获取Openid函数
    getOpenid(data,(result)=>{
        // 保存openid待用
        console.log('用户OpenId:',result.data.openid)
        user.openid = result.data.openid
        res.send({ 'request' : 'ok' })
    })
})

// 监听 3000 端口号 | 127.0.0.1:3000
app.listen('3000',()=>{
    console.log('Server Running ...')
})

// 获取Openid处理函数 - 待写

         getOpenid(data,callback) ,通过req.body可以拿到客户端发过来的请求参数code,结合服务器端的appid参数和secrest参数就可以向往小程序API发起请求返回 Openid进行保存。可以看一下官方文档内容 小程序登录 | 微信开放文档 ,如下请求小程序API

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

         在编写接口之前这三个参数是可以获取到的,我们可以使用这个API接口测试工具测试一下,测试API工具有很多像Postman,Apifox 等这些,然后将这个三个参数携带过去看能够获取这个Openid 。

        在客户端中拿到了code,appid和appsecret已经有了的,测试一下向微信API接口发起请求是否能够拿到Openid.

        下面在服务端安装一个在Vue中常讲到的HTTP数据请求库axios,使用这个可能大家会比较熟悉一些,使用如下命令安装:

npm i axios -S

const express = require('express')
const app = express()
const axios = require('axios')

// 解析表单
// 用户信息
// 模板ID - template_id

app.get('/',(req,res)=>{ ... })

// 获取Openid请求接口
app.get('/getOpenId',(req,res)=>{ ... })

// 监听 3000 端口号 | 127.0.0.1:3000
app.listen(3000,()=>{ ... })

// 获取Openid处理函数 - 通过code获取openid
function getOpenid(data,success){
    axios({
        url:'https://api.weixin.qq.com/sns/jscode2session?appid=' + user.appid + '&secret=' + user.secret + '&js_code=' + data + '&grant_type=authorization_code',
        method: 'GET',
    })
    .then((res)=>{
        // 成功返回 openid 
        // console.log(res.data.openid)
        user.openid = res.data.openid
        success(res)
    })
    .catch((err)=>{ console.log(err) })
}

        通过向微信API请求获取登陆凭证校验返回Openid进行保存。获取Openid之后下面进行发布订阅消息。 


发布订阅消息

        客户端请求 http://127.0.0.1:3000/getOpenId,请求成功之后返回 res.send({ 'request' : 'ok' }),客户端可以进行一个判断成功之后将表单数据进行一个提交。

// index.js - 客户端
  ...
  // 表单提交
  formSubmit(e){
    // e.detail.value 可以拿到用户提交表单的数据
    // console.log(e.detail.value)
    let formInfo = e.detail.value
    formInfo.title = this.data.logArray[formInfo.title]
    wx.login({
      timeout: 2000,
      // 成功后会返回code,将code提交给服务器
      success: res =>{
        // 获取到code
        console.log('获取到code:' + res.code)
        console.log(res)
        // 提交服务器
        wx.request({
            url: 'http://127.0.0.1:3000/getOpenId',
            method: 'POST',
            data: {
                code: res.code
            },
            success: res=>{
                if(res.data.request === 'ok'){
                    wx.request({
                        url: 'http://127.0.0.1:3000/sendTempMsg',
                        methods: 'POST',
                        data:{
                            formInfo : formInfo
                        },
                        success: res=>{
                            if(res.data.request === 'ok'){
                                wx.showToast({
                                    title: '订阅消息发送成功',
                                    icon: 'success'
                                })
                            }
                        }
                    })
                }
            }
        })
      }
    })
  }  
... 

        通过客户端使用wx.login拿到code参数之后向后端发起请求http://127.0.0.1:3000/getOpenId,后端响应请求后并发起请求微信API做登录凭证校验,请求成功后会返回session_key和openid,此时可以将拿到的openid进行保存,同时响应客户端res.send({ 'request': 'ok' }). 下面来编写由服务器发送订阅模板消息的接口。

const express = require('express')
const app = express()
const axios = require('axios')

// 解析表单
// 用户信息
// 模板ID - template_id

app.get('/',(req,res)=>{ ... })

// 获取Openid请求接口
app.get('/getOpenId',(req,res)=>{ ... })

// 发送订阅消息接口
app.post('/sendTempMsg',(req,res)=>{
    //  待写
})

// 监听 3000 端口号 | 127.0.0.1:3000
app.listen(3000,()=>{ ... })

// 获取Openid处理函数 - 通过code获取openid

        客户端提交表单数据信息,在服务器通过req.body.formInfo获取

// 发送订阅消息接口
app.post('/sendTempMsg',(req,res)=>{
    console.log("客户端发来:" ,req.body)
    lef formInfo = req.body.formInfo
    sendTempMsg(formInfo,(result)=>{
        console.log('订阅消息发送结果:', result.data)
        res.send({ 'request': 'ok' })
    })
})


// 发送订阅消息函数处理
sendTempMsg(formInfo,success){
    // 待写
}

        发送订阅消息模板还需要的参数有access_token,access_token是后台接口调用凭据,获取之后可以通过发送订阅消息API实现订阅消息的发送。下面先来获取access_token,这里附上官方文档的获取接口调用凭据 | 微信开放文档

GET https://api.weixin.qq.com/cgi-bin/token

        这里可以使用这个API接口测试工具进行测试,测试是否可以拿到这个access_token.

// 发送订阅消息函数处理
sendTempMsg(formInfo,success){
    // 获取接口调用凭据
    getAccessToken((res)=>{
        // 待写
    }
}

// 获取access_token函数
function getAccessToken(success){
    let url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' +  user.appid + '&secret=' + user.secret
    axios({
        url,
        method: 'GET',
    }).then((res)=>{ 
        // console.log(res.data.access_token) // access_token
        success(res)
    }).catch((err)=>{
        console.log(err)
    })
}

        拿到 access_token 之后,订阅模板内容信息,然后请求微信API接口发送订阅消息。

// 发送订阅消息函数处理
sendTempMsg(formInfo,success){
    // 获取接口调用凭据
    getAccessToken((res)=>{
        // 订阅模板
        var temp = {
            "touser": user.openid,
            "template_id": temp_id,
            "data": {
                "phrase1": { "value": formInfo.title },
                "date2": { "value": formInfo.date },
                "name3": {"value": formInfo.remind },
                "thing4": { "value": formInfo.tip }
            },
            "miniprogram_state": "developer",
            "lang": "zh_CN"
        }
        console.log("模板信息",temp)
        let token = res.data.access_token
        console.log('获取到的access_token:',token)
        // 发送订阅API
        let url = 'https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=' + token
        axios({
            url,
            method: 'POST',
            data: temp
        }).then((res)=>{
            success(res) 
        }).catch((err)=>{ 
            console.log(err)
        })
    }
}

        到这里已经代码已经全部编写完成了,下面来测试一下。


测试效果

1. 启动后台服务器端

        使用 node index.js 命令(安装了 nodemon 可以使用命令 nodemon index.js)

2. 获取用户信息进入界面

3. 填写提醒人获取订阅消息权限  

4. 点击发送,成功会返回提示

5. 查看服务器端信息(发送成功)

6. 查看手机或者电脑微信的服务通知

         到这里我们就完成了对服务器端的编写,如果你仅是对服务器端的内容做了解的话到这里就已经结束了,下面是对客户端的一些内容做调整。


客户端接口封装

        可以看一下客户端的请求方式,下面来将其进行一个接口的封装,这里可以结合之前关于封装接口的文章 : 封装小程序request请求[接口函数]

wx.request({
  url: 'http://127.0.0.1:3000/getOpenId',
  method: 'GET',
  data:{
    code:res.code
  },
  success: res=>{
    console.log(res)
    if(res.data.request === 'ok' && res.statusCode === 200){
      wx.request({
        url: 'http://127.0.0.1:3000/sendTempMsg',
        method:'POST',
        data:{
          formInfo : formInfo
        },
        success: res=>{
          if(res.data.request === 'ok'){
            wx.showToast({
              title: '订阅消息发送成功',
              icon: 'success'
            })
          }
        }
      })
    }
  }
})

         下面来对接口进行一个封装,在util目录下创建request.js文件来封装request请求模块,这里暂不对封装内容进行修改,延用之前已经封装过的模块。

// request.js
// 封装数据请求request
const BASE_URL = 'http://127.0.0.1:3000';  // 基础地址

export default function request(url,params={}){
  return new Promise((resolve,reject)=>{
  wx.showLoading({  // 请求提示
    title: '正在加载中...',
  })
  wx.request({
    url: BASE_URL + url,  // 请求url地址
    data: params.data || {},  // 请求参数
    header: params.header || {},  // 请求头
    method: params.method || 'GET', // 请求方式
    dataType: 'json', // 返回数据类型
    success: (res)=> { // 成功调用
      console.log(res)
      wx.hideLoading(); // 关闭请求提示
      resolve(res.data) // 成功处理res.data中的数据
    },
    fail: (err)=> { // 失败调用
      wx.hideLoading(); // 关闭请求提示
      wx.showToast({  // 提示错误信息
        title: err.errMsg || '请求错误!',
      })
      reject(err) // 失败处理err
    }
  })
})
}

        封装完成之后需要来在 util 目录下来创建 wxApi.js 文件来封装各个接口模块:

// wxApi.js
import request from './request'

// 获取OpenId
export const reqOpenId = (code)=> request('/getOpenId',{ data:{code} })
// 发送订阅消息
export const reqSendTempMsg = (formInfo)=> request('/sendTempMsg',{data:{formInfo},method:'POST'})

        在客户端的 index.js 文件中来引入接口并使用:

const {reqOpenId,reqSendTempMsg } = require('../../utils/wxApi')

...
formSubmit(e){
    ...
        // 提交服务器
        reqOpenId(res.code).then((res)=>{
          if(res.request === 'ok'){
            reqSendTempMsg(formInfo).then((res)=>{
              if(res.request === 'ok'){
                wx.showToast({
                  title: '订阅消息发送成功',
                  icon: 'success'
                })
              }
            })
          }
        })
}

        当然这里还可以更简洁一下,那么剩下就留给读者来进行发挥,感谢大家的支持!!!

猜你喜欢

转载自blog.csdn.net/weixin_52203618/article/details/130312503
今日推荐