前言
上兩篇文章,分別對產品需求和Electron端的實現做了相關的介紹,有興趣的朋友可以看看之前的文章。本文,將介紹服務端的實現。
需求分析
之前的文章也有提到,服務端的主要任務是對資料進行轉發以及生成小程式二維碼。
對資料進行轉發,主要是把使用者在小程式端傳送的訊息,推送給Electron接收端。因此,我們需要建立一個websocket服務。在這裡,我們選擇使用ws模組。
生成小程式二維碼,我們需要使用到微信的API。我們將使用request這個模組向微信伺服器發起相關的請求。
建立websocket服務
入口檔案程式碼如下:
const WebSocketServer = require('ws').Server;
const port = require('./config').port;
const log4js = require('./helpers/logger');
const logger = log4js.Logger('APP');
const wsServer = new WebSocketServer({ port });
logger.info("Server listening on port:" + port);
wsServer.on('connection', function (ws) {
logger.debug('a client connect');
ws.on('message', async function (message) {
// 訊息處理邏輯
});
ws.on('close', function () {
logger.info('ws斷開');
})
ws.on('error', function (error) {
ws.close();
logger.error(error);
})
});
複製程式碼
連線上這個服務的客戶端,有可能是小程式傳送端,也有可能是Electron接收端,所以我們需要對收到的訊息進行了簡單的型別判斷。
根據之前的約定,如果訊息的型別type為INIT,我們就認為是接收端初始化的訊息。傳遞的引數中,包含接收端的UID,我們將會使用這個id作為引數,生成帶引數的小程式二維碼。
由於我們會在公眾場合使用彈幕,所以我們需要對內容進行校驗,防止一些不符合社會主義核心價值觀的言論出現在彈幕中。這裡,我們直接使用微信的msgSecCheck介面。
ws.on('message', async function (message) {
try {
const data = JSON.parse(message);
if (data.type === "INIT") {
logger.info('獲取二維碼', data);
// 將接收端的clientId作為連線的標識
ws.danmuId = data.clientId;
// 獲取微信小程式二維碼
const result = await WechatService.getQrcode(data.clientId);
if(result){
const respMsg = {
type: 'qrcode',
data: result
}
ws.send(JSON.stringify(respMsg));
}
} else {
logger.info('收到訊息', ws.danmuId, message);
// 對訊息內容進行安全性校驗
const isSecurity = await WechatService.checkContentSecurity(data.content);
if(isSecurity){
// 將訊息轉發給對應的接收端
wsServer.clients.forEach(function (client) {
if (client.danmuId === data.scene && client.readyState === 1) {
client.send(message);
}
});
}
}
} catch (error) {
logger.error(error);
}
});
複製程式碼
生成帶引數的小程式二維碼
微信提供生成二維碼的介面有三個,分別應用於不同的應用場景。具體見小程式官方文件
本專案使用getWXACodeUnlimit這個API來獲取小程式碼,通過該介面生成的小程式碼,永久有效,數量暫無限制。
請求地址
POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
複製程式碼
如果呼叫成功,會直接返回圖片***二進位制內容***,如果請求失敗,會返回 JSON 格式的資料。
WTF,官方文件說了,會返回二進位制的內容,但是為什麼我的請求返回亂碼呢?
既然亂碼,那應該就是編解碼的問題了,看看所用的請求模組request是否有相關的引數設定,果不其然:
encoding - encoding to be used on setEncoding of response data. If null, the body is returned as a Buffer. Anything else (including the default value of undefined) will be passed as the encoding parameter to toString() (meaning this is effectively utf8 by default). (Note: if you expect binary data, you should set encoding: null.)
預設是utf8,如果我們需要二進位制內容,那需要將encoding設定為null。結果輸出如下:
看起來是好點了,我們通過Buffer的toString方法將其轉為base64,然後傳給Electron端進行展示,生成二維碼的功能就完成了。access_token獲取與快取
有微信開發經驗的朋友都知道,基本上所有的微信API,請求的時候都需要帶上access_token。而每天請求access_token的次數是有限制的,超出一定的限制則無法獲取,只能坐等明天的到來。所以我們需要對access_token進行快取。
由於我們的專案是單機部署,所以我們可以簡單處理,將access_token存在記憶體中,每次先判斷記憶體中的access_token是否過期,未過期則使用,反之則向微信伺服器請求最新的,並同步更新到記憶體中。
如果考慮多例項部署,可以將access_token儲存到redis中。
日誌列印
魯迅曾經說過,沒有列印日誌的服務都是耍流氓。日誌,是後臺服務不可或缺的一部分,這裡我們使用log4js進行日誌的列印。
最簡單的使用示例:
var log4js = require('log4js');
var logger = log4js.getLogger();
logger.level = 'debug';
logger.debug("Some debug messages");
複製程式碼
詳情請檢視官方文件
GitHub專案地址
我們將陸續整理各端的程式碼放在GitHub上,有興趣的可以查閱一下。
總結
本文使用ws模組搭建了一個簡單的websocket服務,並使用request進行微信API的請求,用於獲取小程式碼等。在日誌列印方面,我們使用log4js,記錄一些關鍵資訊。
下一篇文章,我們將會探討小程式端的實現。
以上,如有錯漏,歡迎指正!
@Author: TDGarden