亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > 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 用戶頭像,最后一個數值代表正方形頭像大小(有0、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教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产欧美一区二区三区久久| 日韩电影中文字幕av| 中文字幕精品久久久久| 欧美在线激情视频| 精品成人国产在线观看男人呻吟| 国产成人亚洲精品| 日韩欧美在线视频观看| 精品香蕉一区二区三区| 日韩在线高清视频| 日本一本a高清免费不卡| 91产国在线观看动作片喷水| 国产成人精品av| 尤物九九久久国产精品的分类| 欧美国产日产韩国视频| 国产成人精品a视频一区www| 色狠狠av一区二区三区香蕉蜜桃| 亚洲精品动漫久久久久| 亚洲人成电影网站色| 久久夜色精品国产亚洲aⅴ| 亚洲精品久久久久中文字幕欢迎你| 精品国偷自产在线视频| 91精品啪在线观看麻豆免费| 在线电影欧美日韩一区二区私密| 久久精品中文字幕一区| 欧美亚洲成人网| 国产精品免费小视频| 国产精品久久久久久影视| 欧美日韩亚洲视频一区| 久久精品欧美视频| 91日本视频在线| 91精品在线看| 欧美日韩ab片| 成人黄色免费网站在线观看| 久久中文久久字幕| 亚洲免费视频在线观看| 色琪琪综合男人的天堂aⅴ视频| 91高清视频在线免费观看| 欧美激情一区二区三区久久久| 日本欧美在线视频| 96pao国产成视频永久免费| 69久久夜色精品国产69乱青草| 5278欧美一区二区三区| 欧美性色xo影院| 日韩av免费网站| 菠萝蜜影院一区二区免费| 日韩精品视频观看| 亚洲国产一区自拍| 色樱桃影院亚洲精品影院| 日韩欧美一区二区在线| 成人乱色短篇合集| 欧美激情图片区| 国产大片精品免费永久看nba| 欧美中文在线观看| 国产欧美va欧美va香蕉在| 中文字幕亚洲一区| 一本大道香蕉久在线播放29| 国产欧美日韩中文字幕在线| 国产精品久久一区主播| 不卡av日日日| 亚洲小视频在线| 精品毛片三在线观看| 91av在线播放视频| 国产成人精品久久二区二区| 国产欧美日韩最新| 国产日本欧美视频| 91精品国产综合久久香蕉最新版| 韩日欧美一区二区| 日韩高清电影免费观看完整| 欧美成人精品在线视频| 久久久久久美女| 亚洲成av人片在线观看香蕉| 欧美高清视频免费观看| 国产一区二区三区丝袜| 92看片淫黄大片看国产片| 精品久久久一区二区| 欧美另类暴力丝袜| 久久久999精品| 97不卡在线视频| 久久深夜福利免费观看| 成人444kkkk在线观看| 欧美黑人狂野猛交老妇| 亚洲视频在线播放| 日韩精品在线第一页| 久久亚洲精品一区| 日韩天堂在线视频| 国产精品热视频| 国产成人综合亚洲| 国产精品海角社区在线观看| 欧美日韩一区二区免费在线观看| 色综合天天综合网国产成人网| 欧美激情视频在线观看| 91精品国产九九九久久久亚洲| 狠狠操狠狠色综合网| 91精品国产91久久久久久最新| 91超碰中文字幕久久精品| 97视频在线观看免费高清完整版在线观看| 国产精品免费电影| 亚洲欧洲免费视频| 亚洲精品一区二区三区不| 国产精品永久免费视频| 4438全国亚洲精品在线观看视频| 国产欧美精品久久久| 91精品久久久久久久久久另类| 欧美激情久久久久久| 91精品国产综合久久香蕉| 91免费在线视频网站| 色噜噜狠狠狠综合曰曰曰| 91热精品视频| 97在线视频免费看| 亚洲人成网站免费播放| 欧美中文在线观看国产| 欧美裸体xxxxx| 欧美日韩性生活视频| 欧美性感美女h网站在线观看免费| 26uuu国产精品视频| 亚洲国产免费av| 欧美第一淫aaasss性| 日韩亚洲欧美中文在线| 欧美专区国产专区| 久久97精品久久久久久久不卡| 亚洲一区二区三区视频播放| 亚洲国产欧美在线成人app| 亚洲欧洲中文天堂| 国产亚洲aⅴaaaaaa毛片| 中文字幕在线日韩| 日本一区二区三区四区视频| 国产精品伦子伦免费视频| 欧美一级视频在线观看| 国精产品一区一区三区有限在线| 4438全国成人免费| 欧美精品在线免费观看| 欧美激情精品久久久久久蜜臀| 蜜臀久久99精品久久久无需会员| 国产亚洲aⅴaaaaaa毛片| 91老司机在线| 欧美一级片久久久久久久| 亚洲精品福利在线观看| 国产亚洲欧美另类中文| 日韩精品免费在线视频| 岛国av一区二区| 亚洲欧美国产va在线影院| 久热国产精品视频| 国产精品视频一区二区高潮| 亲爱的老师9免费观看全集电视剧| 成人乱色短篇合集| 欧美激情一区二区三区久久久| 日韩毛片在线观看| 国产一区欧美二区三区| 久久精品国产一区二区电影| 欧美高清视频在线观看| 欧美高清不卡在线| 欧美xxxx14xxxxx性爽| 欧美激情区在线播放| 欧美日韩国产一中文字不卡| 性夜试看影院91社区| 粗暴蹂躏中文一区二区三区| 欧美一区二区三区艳史| 亚洲国产欧美一区| 色久欧美在线视频观看| 国产精品久久久久91| 在线亚洲国产精品网| 国产精品日韩电影| 亚洲一级黄色av| 91av国产在线|