国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > JS > 正文

使用ThinkJs搭建微信中控服務的實現方法

2024-05-06 16:54:06
字體:
來源:轉載
供稿:網友

本人前端渣渣一枚,這篇文章是第一次寫,如果有硬核bug,請大佬們輕噴、指出... 另外,本文不涉及任何接口安全、參數校驗之類的東西,默認對調用方無腦級的信任:joy: 目前自用的接口包括但不限于以下這些

|--- 微信相關| |--- 0. 處理微信推過來的一些消息| |--- 1. 獲取微信SDK配置參數| |--- 2. 微信鑒權登陸| |--- 3. 獲取微信用戶信息| |--- 4. 獲取AccessToken| |--- 5. 批量發送模版消息| |--- 6. 獲取模版消息列表| |--- 7. 批量發送客服消息

背景

  • 【需求】小項目很多很雜,而且大部分需求都是基于微信開發的,每次都查微信文檔的話就會很郁悶:unamused:...
  • 【號多】公眾號超級多,項目中偶爾會涉及借權獲取用戶信息(在不綁定微信開放平臺的前提下,需要臨時自建各個公眾號的openid關聯關系),類似這樣同時需要不止一個公眾號配合來完成一件事的需求,就容易把人整懵逼...
  • 【支付】微信支付的商戶號也很多,而且有時候支付需要用的商戶號,還不能用關聯的公眾號取出來的openid去支付...
  • 【官方】微信官方文檔建議!把獲取AccessToken等微信API抽離成單獨的服務... 等等等等........所以...:joy:

創建ThinkJS項目

官網

thinkjs.org/

簡介

ThinkJS 是一款面向未來開發的 Node.js 框架,整合了大量的項目最佳實踐,讓企業級開發變得如此簡單、高效。從 3.0 開始,框架底層基于 Koa 2.x 實現,兼容 Koa 的所有功能。

安裝腳手架

$ npm install -g think-cli

創建及啟動項目

$ thinkjs new demo;$ cd demo;$ npm install; $ npm start; 

目錄結構

|--- development.js  //開發環境下的入口文件|--- nginx.conf //nginx 配置文件|--- package.json|--- pm2.json //pm2 配置文件|--- production.js //生產環境下的入口文件|--- README.md|--- src| |--- bootstrap //啟動自動執行目錄 | | |--- master.js //Master 進程下自動執行| | |--- worker.js //Worker 進程下自動執行| |--- config //配置文件目錄| | |--- adapter.js // adapter 配置文件 | | |--- config.js // 默認配置文件 | | |--- config.production.js //生產環境下的默認配置文件,和 config.js 合并 | | |--- extend.js //extend 配置文件 | | |--- middleware.js //middleware 配置文件 | | |--- router.js //自定義路由配置文件| |--- controller //控制器目錄 | | |--- base.js| | |--- index.js| |--- logic //logic 目錄| | |--- index.js| |--- model //模型目錄| | |--- index.js|--- view //模板目錄| |--- index_index.html

安裝think-wechat插件

介紹

微信中間件,基于 node-webot/wechat,支持 thinkJS 3.0

安裝

$ npm install think-wechat --save

$ cnpm install think-wechat --save

配置

文件:/src/config/middleware.js

const wechat = require('think-wechat')module.exports = [  ...   {    handle: wechat,    match: '/index',    options: {      token: '', // 令牌,和公眾號/基本配置/服務器配置里面寫一樣的即可      appid: '', // 這里貌似可以隨便填,因為我們后面要用數據庫配置多個公眾號      encodingAESKey: '',      checkSignature: false    }  }, {    handle: 'payload', // think-wechat 必須要在 payload 中間件前面加載,它會代替 payload 處理微信發過來的 post 請求中的數據。    options: {      keepExtensions: true,      limit: '5mb'    }  },]

注:match下我這里寫的是 /index ,對應的項目文件是 /src/controller/index.js ,對應的公眾號后臺所需配置的服務器地址就是 http(https)://域名:端口/index

創建數據庫和相關表

我這里創建了三個微信的相關表。

配置表:wx_config

 

字段 類型 說明
id int 主鍵
name varchar 名稱
appid varchar appid
secret varchar secret

 

用戶表:wx_userinfo

 

字段 類型 注釋
id int 主鍵
subscribe int 用戶是否訂閱該公眾號標識,值為0時,代表此用戶沒有關注該公眾號,拉取不到其余信息。
nickname varchar 用戶的昵稱
sex int 用戶的性別,值為1時是男性,值為2時是女性,值為0時是未知
language varchar 用戶所在省份
city varchar 用戶所在城市
province varchar 用戶所在省份
country varchar 用戶所在國家
headimgurl longtext 用戶頭像,最后一個數值代表正方形頭像大?。ㄓ?、46、64、96、132數值可選,0代表640*640正方形頭像),用戶沒有頭像時該項為空。若用戶更換頭像,原有頭像URL將失效。
subscribe_time double 用戶關注時間,為時間戳。如果用戶曾多次關注,則取最后關注時間
unionid varchar 只有在用戶將公眾號綁定到微信開放平臺帳號后,才會出現該字段。
openid varchar 用戶的標識,對當前公眾號唯一
wx_config_id int 對應配置的微信號id

 

模版消息日志表:wx_template_log

 

字段 類型 注釋
id int 主鍵
template_id varchar 模版id
openid varchar 用戶的標識,對當前公眾號唯一
url varchar 跳轉url
miniprogram varchar 跳轉小程序
data varchar 發送內容json字符串
add_time double 添加時間戳
send_time double 發送時間戳
send_status varchar 發送結果
wx_config_id double 對應配置的微信號id
uuid varchar 本次發送的uuid,業務系統可通過uuid查詢模版消息推送結果

 

處理微信推送消息

文件目錄

/src/controller/index.js

文件內容

module.exports = class extends think.Controller {  /*  * 入口:驗證開發者服務器  * 驗證開發者服務器,這里只是演示,所以沒做簽名校驗,實際上應該要根據微信要求進行簽名校驗  */  async indexAction() {    let that = this;    if (that.method != 'REPLY') {      return that.json({code: 1, msg: '非法請求', data: null})    }    const {echostr} = that.get();    return that.end(echostr);  }     /*  * 文字  * 用于處理微信推過來的文字消息  */  async textAction() {    let that = this;    let {id, signature, timestamp, nonce, openid} = that.get();    let {ToUserName, FromUserName, CreateTime, MsgType, Content, MsgId} = that.post();    .....    that.success('')  }    /*  * 事件  * 用于處理微信推過來的事件消息,例如點擊菜單等  */  async eventAction() {    let that = this;    let {id, signature, timestamp, nonce, openid} = that.get();    let {ToUserName, FromUserName, CreateTime, MsgType, Event, EventKey, Ticket, Latitude, Longitude, Precision} = that.post();    switch (Event) {      case 'subscribe': // 關注公眾號        ...        break;      case 'unsubscribe': // 取消關注公眾號        ...        break;      case 'SCAN': // 已關注掃碼        ...        break;      case 'LOCATION': // 地理位置        ...        break;      case 'CLICK': // 自定義菜菜單        ...        break;      case 'VIEW': // 跳轉        ...        break;      case 'TEMPLATESENDJOBFINISH':// 模版消息發送完畢        ...        break;    }     that.success('')  }}

注:支持的action包括: textAction 、 imageAction 、 voiceAction 、 videoAction 、 shortvideoAction、 locationAction 、 linkAction 、 eventAction 、 deviceTextAction 、 deviceEventAction 。

公眾號后臺配置

 

 
ThinkJs,微信中控服務

 

注:后面跟的id參數是為了區分是哪個公眾號推過來的消息,在上面的接口參數中也有體現

微信相關API的編寫

目錄結構

|--- src| |--- controller //控制器目錄 | | |--- index.js // 處理微信推送的消息,上面有寫到| | |--- common.js // 一些公共方法| | |--- open // 開放給其他業務服務的api接口| | | |--- wx.js| | |--- private // 放一些內部調用的方法,調用微信api的方法主要在這里面| | | |--- wx.js

這個目錄結構可能不太合理,后期再改進吧:grin:

公共方法

// src/controller/common.jsimport axios from 'axios'import {baseSql} from "./unit";module.exports = class extends think.Controller {  // 獲取appinfo  async getWxConfigById(id) {    let that = this;    let data = await that.cache(`wx_config:wxid_${id}`, async () => {      // 數據庫內取      let info = await that.model('wx_config', baseSql).where({id: id}).find();      if (!think.isEmpty(info)) {        return info      }    })    return data || {}  }  // 獲取access_token  async getAccessToken(id) {    let that = this;    let accessToken = await that.cache(`wx_access_token:wxid_${id}`, async () => {      let {appid, secret} = await that.getWxConfigById(id);      let {data} = await axios({        method: 'get',        url: `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`      });      return data.access_token    });    return accessToken  }}

接口過濾器

所有開放出來的接口的前置方法,俗稱過濾器?所有開放的接口必傳get參數是 wxid ,對應數據庫表wx_config里面 id

// src/controller/open/wx.jsasync __before() {  let that = this;  let wxid = that.get('wxid');  if (think.isEmpty(wxid)) {    return that.json({code: 1, msg: 'wxid不存在'})  }  that.wxConfig = await that.controller('common').getWxConfigById(wxid);  if (think.isEmpty(that.wxConfig)) {    return that.json({code: 1, msg: 'wxid不存在'})  }}

接口 - 獲取AccessToken

代碼

// src/controller/open/wx.jsasync get_access_tokenAction() {  let that = this;  let accessToken = await that.controller('common').getAccessToken(that.wxConfig.id);  return that.json({code: 0, msg: '', data: {access_token: accessToken}})}

文檔

 

 
ThinkJs,微信中控服務 

 

接口 - 獲取微信sdk的config

代碼

// src/controller/open/wx.jsasync get_wxsdk_configAction() {  let that = this;  let {url} = that.get();  if (think.isEmpty(url)) {    return that.json({code: 1, msg: '參數不正確'})  }  let sdkConfig = await that.controller('private/wx').getSdkConfig(that.wxConfig.id, url);  return that.json({code: 0, msg: '', data: sdkConfig})}// src/controller/private/wx.jsconst sha1 = require('sha1');const getTimestamp = () => parseInt(Date.now() / 1000)const getNonceStr = () => Math.random().toString(36).substr(2, 15)const getSignature = (params) => sha1(Object.keys(params).sort().map(key => `${key.toLowerCase()}=${params[key]}`).join('&'));async getSdkConfig(id, url) {  let that = this;  let {appid} = await that.controller('common').getWxConfigById(id);  let shareConfig = {    nonceStr: getNonceStr(),    jsapi_ticket: await that.getJsapiTicket(id),    timestamp: getTimestamp(),    url: url  }  return {    appId: appid,    timestamp: shareConfig.timestamp,    nonceStr: shareConfig.nonceStr,    signature: getSignature(shareConfig)  }}

文檔

 

 
ThinkJs,微信中控服務 

 

接口 - 獲取UserInfo

代碼

// src/controller/open/wx.jsasync get_userinfoAction() {  let that = this;  let {openid} = that.get();  if (think.isEmpty(openid)) {    return that.json({code: 1, msg: '參數不正確'})  }  let userInfo = await that.controller('private/wx').getUserInfo(that.wxConfig.id, openid);  if (think.isEmpty(userInfo)) {    return that.json({code: 1, msg: 'openid不存在', data: null})  }  return that.json({code: 0, msg: '', data: userInfo})}// src/controller/private/wx.jsasync getUserInfo(id, openid) {  let that = this;  let userInfo = await that.cache(`wx_userinfo:wxid_${id}:${openid}`, async () => {    //先取數據庫    let model = that.model('wx_userinfo', baseSql);    let userInfo = await model.where({wx_config_id: id, openid: openid}).find();    if (!think.isEmpty(userInfo) && userInfo.subscribe == 1 && userInfo.unionid != null) {      return userInfo    }    //如果數據庫內沒有,取新的存入數據庫    let accessToken = await that.controller('common').getAccessToken(id);    let url = `https://api.weixin.qq.com/cgi-bin/user/info?access_token=${accessToken}&openid=${openid}&lang=zh_CN`;    let {data} = await axios({method: 'get', url: url});    if (data.openid) {      //命中修改,沒有命中添加      let resId = await model.thenUpdate(        Object.assign(data, {wx_config_id: id}),        {openid: openid, wx_config_id: id});      return await model.where({id: resId}).find();    }  })  return userInfo}

文檔

 

 
ThinkJs,微信中控服務 

 

接口 - 批量發送文字客服消息

代碼

// src/controller/open/wx.jsasync send_msg_textAction() {  let that = this;  let {list} = that.post();  if (think.isEmpty(list)) {    return that.json({code: 1, msg: '參數不正確'})  }  that._sendMsgTextList(that.wxConfig.id, list);  return that.json({code: 0, msg: '', data: null}) }  async _sendMsgTextList(wxid, list) {  let that = this;  let apiWxController = that.controller('private/wx');  for (let item of list) {    let data = await apiWxController.sendMsgText(wxid, item.openid, item.text)  }}// src/controller/private/wx.jsasync sendMsgText(id, openid, content) {  let that = this;  let accessToken = await that.controller('common').getAccessToken(id);  let url = `https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${accessToken}`  let {data} = await axios({    method: 'post', url: url, data: {"msgtype": 'text', "touser": openid, "text": {"content": content}}  })  return data;}

文檔

 

 
ThinkJs,微信中控服務 

 

寫在結尾

其實還有很多接口,這里就不全部列出來了。

應該能看出來,在這個項目里面并不僅僅是把微信的接口做了個簡單的轉發,而是有一些自己的處理邏輯在里面。

比如獲取微信用戶信息的時候,會先判斷緩存里有沒有,如果沒有就取數據庫,如果還沒有再去微信的接口??;如果數據庫有,并且關注字段是未關注的話,還是會調用微信的接口取一波再更新。 反正一天內,微信接口的調用次數是絕對夠用的。

再比如批量發送模版消息,中控服務在收到請求后會先創建一個uuid,要發的模版消息全部保存到數據庫內,直接把uuid返給調用方。 然后中控會異步用uuid取出來這批模版消息,一個一個發,一個一個更新結果。 這樣在業務方調用發送模版消息之后,無需等待全部發送完畢,就可以用拿到的uuid,去中控查詢這次批量發送的狀態結果。

目前是綁了七八個公眾號,在沒燒過香的前提下,還沒出過什么問題

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 大化| 兴义市| 彭泽县| 灵山县| 望江县| 湖州市| 凤台县| 比如县| 福建省| 青神县| 文山县| 新安县| 新巴尔虎左旗| 永嘉县| 云霄县| 工布江达县| 澄江县| 沧州市| 成安县| 临江市| 阳泉市| 泽普县| 东平县| 武清区| 旬邑县| 浦城县| 澄迈县| 渭源县| 芜湖市| 甘孜| 海盐县| 阳西县| 富源县| 兴仁县| 保靖县| 富阳市| 中卫市| 临朐县| 黄大仙区| 淅川县| 平远县|