欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用ThinkJs搭建微信中控服務(wù)的實現(xiàn)方法

 更新時間:2019年08月08日 08:34:31   作者:嘟小乾  
這篇文章主要介紹了使用ThinkJs搭建微信中控服務(wù)的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

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

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

背景

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

創(chuàng)建ThinkJS項目

官網(wǎng)

thinkjs.org/

簡介

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

安裝腳手架

$ npm install -g think-cli

創(chuàng)建及啟動項目

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

目錄結(jié)構(gòu)

|--- development.js  //開發(fā)環(huán)境下的入口文件
|--- nginx.conf //nginx 配置文件
|--- package.json
|--- pm2.json //pm2 配置文件
|--- production.js //生產(chǎn)環(huán)境下的入口文件
|--- README.md
|--- src
| |--- bootstrap //啟動自動執(zhí)行目錄 
| | |--- master.js //Master 進程下自動執(zhí)行
| | |--- worker.js //Worker 進程下自動執(zhí)行
| |--- config //配置文件目錄
| | |--- adapter.js // adapter 配置文件 
| | |--- config.js // 默認配置文件 
| | |--- config.production.js //生產(chǎn)環(huán)境下的默認配置文件,和 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: '', // 令牌,和公眾號/基本配置/服務(wù)器配置里面寫一樣的即可
      appid: '', // 這里貌似可以隨便填,因為我們后面要用數(shù)據(jù)庫配置多個公眾號
      encodingAESKey: '',
      checkSignature: false
    }
  }, {
    handle: 'payload', // think-wechat 必須要在 payload 中間件前面加載,它會代替 payload 處理微信發(fā)過來的 post 請求中的數(shù)據(jù)。
    options: {
      keepExtensions: true,
      limit: '5mb'
    }
  },
]

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

創(chuàng)建數(shù)據(jù)庫和相關(guān)表

我這里創(chuàng)建了三個微信的相關(guān)表。

配置表:wx_config

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

用戶表:wx_userinfo

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

模版消息日志表:wx_template_log

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

處理微信推送消息

文件目錄

/src/controller/index.js

文件內(nèi)容

module.exports = class extends think.Controller {
  /*
  * 入口:驗證開發(fā)者服務(wù)器
  * 驗證開發(fā)者服務(wù)器,這里只是演示,所以沒做簽名校驗,實際上應(yīng)該要根據(jù)微信要求進行簽名校驗
  */
  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': // 關(guān)注公眾號
        ...
        break;
      case 'unsubscribe': // 取消關(guān)注公眾號
        ...
        break;
      case 'SCAN': // 已關(guān)注掃碼
        ...
        break;
      case 'LOCATION': // 地理位置
        ...
        break;
      case 'CLICK': // 自定義菜菜單
        ...
        break;
      case 'VIEW': // 跳轉(zhuǎn)
        ...
        break;
      case 'TEMPLATESENDJOBFINISH':// 模版消息發(fā)送完畢
        ...
        break;
    } 
    that.success('')
  }
}

注:支持的action包括: textAction 、 imageActionvoiceAction 、 videoActionshortvideoAction 、 locationActionlinkAction 、 eventAction 、 deviceTextAction 、 deviceEventAction 。

公眾號后臺配置

注:后面跟的id參數(shù)是為了區(qū)分是哪個公眾號推過來的消息,在上面的接口參數(shù)中也有體現(xiàn)

微信相關(guān)API的編寫

目錄結(jié)構(gòu)

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

這個目錄結(jié)構(gòu)可能不太合理,后期再改進吧:grin:

公共方法

// src/controller/common.js

import 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 () => {
      // 數(shù)據(jù)庫內(nèi)取
      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參數(shù)是 wxid ,對應(yīng)數(shù)據(jù)庫表wx_config里面 id

// src/controller/open/wx.js

async __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.js

async 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}})
}

文檔

 

接口 - 獲取微信sdk的config

代碼

// src/controller/open/wx.js

async get_wxsdk_configAction() {
  let that = this;
  let {url} = that.get();
  if (think.isEmpty(url)) {
    return that.json({code: 1, msg: '參數(shù)不正確'})
  }
  let sdkConfig = await that.controller('private/wx').getSdkConfig(that.wxConfig.id, url);
  return that.json({code: 0, msg: '', data: sdkConfig})
}


// src/controller/private/wx.js

const 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)
  }
}

文檔

 

接口 - 獲取UserInfo

代碼

// src/controller/open/wx.js

async get_userinfoAction() {
  let that = this;
  let {openid} = that.get();
  if (think.isEmpty(openid)) {
    return that.json({code: 1, msg: '參數(shù)不正確'})
  }
  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.js

async getUserInfo(id, openid) {
  let that = this;
  let userInfo = await that.cache(`wx_userinfo:wxid_${id}:${openid}`, async () => {
    //先取數(shù)據(jù)庫
    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
    }
    //如果數(shù)據(jù)庫內(nèi)沒有,取新的存入數(shù)據(jù)庫
    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
}

文檔

 

接口 - 批量發(fā)送文字客服消息

代碼

// src/controller/open/wx.js

async send_msg_textAction() {
  let that = this;
  let {list} = that.post();
  if (think.isEmpty(list)) {
    return that.json({code: 1, msg: '參數(shù)不正確'})
  }
  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.js

async 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;
}

文檔

 

寫在結(jié)尾

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

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

比如獲取微信用戶信息的時候,會先判斷緩存里有沒有,如果沒有就取數(shù)據(jù)庫,如果還沒有再去微信的接口?。蝗绻麛?shù)據(jù)庫有,并且關(guān)注字段是未關(guān)注的話,還是會調(diào)用微信的接口取一波再更新。 反正一天內(nèi),微信接口的調(diào)用次數(shù)是絕對夠用的。

再比如批量發(fā)送模版消息,中控服務(wù)在收到請求后會先創(chuàng)建一個uuid,要發(fā)的模版消息全部保存到數(shù)據(jù)庫內(nèi),直接把uuid返給調(diào)用方。 然后中控會異步用uuid取出來這批模版消息,一個一個發(fā),一個一個更新結(jié)果。 這樣在業(yè)務(wù)方調(diào)用發(fā)送模版消息之后,無需等待全部發(fā)送完畢,就可以用拿到的uuid,去中控查詢這次批量發(fā)送的狀態(tài)結(jié)果。

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 通過JS 獲取Mouse Position(鼠標坐標)的代碼

    通過JS 獲取Mouse Position(鼠標坐標)的代碼

    最近我發(fā)現(xiàn)在webpage中獲取空間的絕對坐標時,如果有滾動條就會有錯,后來用無名發(fā)現(xiàn)的方法得以解決。
    2009-09-09
  • 原生js實現(xiàn)移動端觸摸輪播的示例代碼

    原生js實現(xiàn)移動端觸摸輪播的示例代碼

    下面小編就為大家分享一篇原生js實現(xiàn)移動端觸摸輪播的示例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • layui使用form表單實現(xiàn)post請求頁面跳轉(zhuǎn)的方法

    layui使用form表單實現(xiàn)post請求頁面跳轉(zhuǎn)的方法

    今天小編就為大家分享一篇layui使用form表單實現(xiàn)post請求頁面跳轉(zhuǎn)的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • PNG背景在不同瀏覽器下的應(yīng)用

    PNG背景在不同瀏覽器下的應(yīng)用

    論壇中kayeun在我的版面發(fā)了一個問題,關(guān)于PNG透明背景在FIREFOX瀏覽器下顯示異常。 這里談一下不同瀏覽器內(nèi),如何應(yīng)用PNG圖片作背景。
    2009-06-06
  • JavaScript reduce方法使用方法介紹

    JavaScript reduce方法使用方法介紹

    Reduce是個純函數(shù),即只要傳入相同的參數(shù),每次都應(yīng)返回相同的結(jié)果。不要把和處理數(shù)據(jù)無關(guān)的代碼放在Reduce里,讓Reduce保持純凈,只是單純地執(zhí)行計算,這篇文章主要介紹了Redux拆分reduce函數(shù)流程
    2022-10-10
  • JavaScript 錯誤捕獲與處理的完整指南

    JavaScript 錯誤捕獲與處理的完整指南

    在JavaScript中捕獲錯誤通常有四種方式,try-catch 語句塊,Promise 的 catch 方法,throw 語句以及window.onerror事件處理程序,并通過代碼示例給大家講解的非常詳細,需要的朋友可以參考下
    2024-02-02
  • 多個jquery.datatable共存,checkbox全選異常的快速解決方法

    多個jquery.datatable共存,checkbox全選異常的快速解決方法

    這篇文章主要介紹了多個jquery.datatable共存,checkbox全選異常的快速解決方法。需要的朋友可以過來參考下,希望對大家有所幫助
    2013-12-12
  • JavaScript的內(nèi)存釋放問題詳解

    JavaScript的內(nèi)存釋放問題詳解

    這篇文章主要介紹了JavaScript的內(nèi)存釋放問題詳解的相關(guān)資料,需要的朋友可以參考下
    2015-01-01
  • Bootstrap table的使用方法

    Bootstrap table的使用方法

    這篇文章主要為大家詳細解析了JS組件Bootstrap Table使用方法,具有一定的實用性和參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • 微信小程序項目實踐之主頁tab選項實現(xiàn)

    微信小程序項目實踐之主頁tab選項實現(xiàn)

    這篇文章主要介紹了微信小程序項目實踐之主頁tab選項實現(xiàn),本文通過實例代碼相結(jié)合的形式給大家介紹的非常詳細,需要的朋友可以參考下
    2018-07-07

最新評論