前言
如果你不知道 企業微信側邊欄 是什麼,那就可以划走這篇文章了。如果你知道這是個啥,那你一定非常苦惱要怎麼開始。
從去年就開始就一直有在做企業微信側邊欄的應用。說實話,開發和除錯體驗實在是太糟糕了,而且上手的時候跟本連怎麼開啟它都不知道。
最難頂的是官方文件也寫得不仔細,連個最簡單能跑的 Demo 都沒有,找開發討論區吧,官方的客服也不太懂程式碼,問了等於沒問,很多貼子都是網友互助的。
當然本文也不是簡單的水文,所以下面簡單來聊聊 企業微信側邊欄 一些重要的部分吧。
是什麼
企業微信側邊欄(下稱企微側欄)其實就是企業微信右邊的一個側欄(WebView)。
但是並不是所有對話(session)都能開啟這個側邊欄的,只有在 外部聯絡人 和 外部聯絡群 的對話中才能右下角開啟側欄的按鈕。
那 外部聯絡人 和 外部聯絡群 又是個啥?為什麼只有在這種情況下才能開啟呢?這就要說到側欄到底要解決的什麼問題。
為什麼
側欄真正要解決的痛點其實是 社群/客戶運營和管理。
可能大家微信上只有 300 左右的好友,但是有沒有想過如果作為一個銷售人員,他可能需要新增 1000+ 的客戶,要建立 100+ 的群聊,每天可能都會收到成百上千條訊息。
這些客戶可能被分為:想買產品的、有意願想買產品的、已經買產品的、普通客戶、VIP、SVIP等。
群聊可能被分為:2020年11月學習群、VIP 群、粉絲群等等。
這麼多的客戶和群聊,對於單一個銷售人員來說就非常頭疼。很容易就忘記這個客戶是哪個分割槽、哪個類別、哪種標籤的。
而且銷售人員主要的工作就是要精細化運營、每天都要和客戶以及群聊 聊天。什麼時候聊、怎麼聊、聊什麼都是大學問,而且一旦和這麼多客戶、群聊聊天更是難上加難。類比一下,時間管理大師最多也只能和 10 個人聊也已經頂天了。
所以企業微信就想:能不能在聊天會話當中有一個工具箱,銷售人員就可以在這個工具箱裡檢視客戶/群聊的業務陣列,或者透過這個工具箱更好地運營。這就是側邊欄的由來。
上面的這些 “客戶” 和 “群聊” 則這被稱為:外部聯絡人 和 外部聯絡群,這裡的 “外部” 指的就是 不是自己企業內部員工。
側欄應用架構
側邊欄本質上也是一個 WebView,所以我們只需要寫好前端,無論是普通 html 或者 SPA App 都能被放在側邊欄上。
但是普通的前端還是不夠的,如果你想和 企業微信 進行一定的互動,比如發訊息、立即建立群聊、打開個人資訊彈窗,那就需要企業微信提供的 JS-SDK,具體文件請看這裡。
可是 JS-SDK 是需要先 config 才能正常使用,而 config 的引數需要從 企業微信服務端 獲取 jsapi_ticket 來生成 signature 才能正常初始化 JS-SDK。
wx.agentConfig({
corpid: '', // 必填,企業微信的corpid,必須與當前登入的企業一致
agentid: '', // 必填,企業微信的應用id (e.g. 1000247)
timestamp: , // 必填,生成簽名的時間戳
nonceStr: '', // 必填,生成簽名的隨機串
signature: '',// 必填,簽名,見附錄-JS-SDK使用許可權簽名演算法
jsApiList: ['selectExternalContact'], //必填,傳入需要使用的介面名稱
success: function(res) {
// 回撥
},
fail: function(res) {
if(res.errMsg.indexOf('function not exist') > -1){
alert('版本過低請升級')
}
}
});
複製程式碼
然後問題又來了,從 企業微信服務端 獲取 jsapi_ticket 又需要 access_token 這個關鍵引數,而 access_token 又不能直接快取到前端,因此,還需要另外一個後端(中間層)來快取 access_token,並提供 企業微信服務端 API 的轉發服務。
所以,總得來說,側邊欄看似是前端的東西,但其實它的基礎架構起碼有 側邊欄、業務服務端 和 企微服務端。
企微的服務端已經由企業微信提供了,那我們要實現的就是 側邊欄 和 業務服務端 了。如果你是第一次搞側邊欄,一定會被弄得非常煩,所以建議 Fork 我的 側邊欄(前端)模板 和 後端模板,然後在這基礎上進行修改。
開發關鍵部分
因為這裡面的細節太多了,想了解具體實現還是去看那兩個模板,這裡僅講一些比較重要的點。
轉發服務
首先來說這個轉發服務吧,需要對 企微服務端 API 進行轉發,而服務端的請求是需要 access_token 作為重要引數來轉的,但是 access_token 不能一直請求獲取,所以需要進行 redis 快取。
而 redis 又需要 Docker 來啟動,不得不說為了做個簡單網頁,連 Docker 都整上了:
version: '3'
services:
redis:
image: redis
container_name: 'wecom-sidebar-redis'
ports:
- "6379:6379"
restart: always
複製程式碼
轉發服務在模板裡我使用簡單的 Koa 實現,redis 客戶端的 NPM 包,可以使用 ioredis 來快取。
轉發就很簡單了,直接使用 axios 就可以了,但是要注意 proxy 要設定為 false,不然會報錯:
const axios = require('axios')
const baseURL = 'https://qyapi.weixin.qq.com/cgi-bin';
const proxy = axios.create({
baseURL,
proxy: false // 不指定會報錯 SSL routines:ssl3_get_record:wrong version number,參考:https://github.com/guzzle/guzzle/issues/2593
})
module.exports = proxy
複製程式碼
具體的轉發呼叫 API 相信大家都會怎麼寫就不多說了。
重定向獲取 userId
這種 userId 的獲取機制和微信網頁開發是差不多的,需要先重定向某個 url,然後從 search 引數獲取 code,再用這個 code 透過上面的轉發服務向企業微信服務端換取 userId,具體實現可以看 文件這裡。
/**
* 獲取重定位的 OAuth 路徑
* @returns {string}
*/
const generateOAuthUrl = (config: Config) => {
const [redirectUri] = window.location.href.split('#');
const searchObj = {
appid: config.corpId,
redirect_uri: encodeURIComponent(redirectUri),
response_type: 'code',
scope: 'snsapi_base',
agentid: config.agentId,
state: 'A1',
};
const search = Object.entries(searchObj)
.map(entry => {
const [key, value] = entry;
return `${key}=${value}`;
})
.join('&');
return `https://open.weixin.qq.com/connect/oauth2/authorize?${search}#wechat_redirect`;
};
/**
* 判斷當前網頁是否需要重定向
*/
const checkRedirect = async (config: Config, getUserId: GetUserId) => {
if (isMock) return
const userId = Cookies.get('userId')
const unAuth = !userId || userId === 'undefined' || userId === 'null'
const codeExist = window.location.search.includes('code');
// 判斷是否需要重定向
if (unAuth && !codeExist) {
window.location.replace(generateOAuthUrl(config));
}
// 判斷是否需要重新獲取 userId
if (unAuth) {
const code = qs.parse(window.location.search.slice(1)).code as string
const newUserId = await getUserId(code)
Cookies.set('userId', newUserId)
}
};
複製程式碼
JS-SDK 初始化
這應該是使用客戶端 API 時最重要的一個步驟了,而企微的 JS-SDK API 用起來很麻煩,所以我將它的 API 又再度封裝了一下,主要做的是 promisify 的操作:
const agentConfig = (agentSetting: Omit<wx.AgentConfigParams, 'success' | 'fail'>): Promise<wx.WxFnCallbackRes> => {
return new Promise((resolve, reject) => {
wx.agentConfig({
...agentSetting,
success: resolve,
fail: reject,
});
});
};
複製程式碼
然後從我們的轉發服務獲取 signature 並呼叫 agentConfig(3.0.24 以上可以不呼叫 config),
const initSdk = async (config: Config, getSignatures: GetSignatures) => {
const { corpId, agentId } = config;
// 獲取 ticket
const signaturesRes = await getSignatures();
const agentConfigRes = await jsSdk.agentConfig({
corpid: corpId,
agentid: agentId,
timestamp: signaturesRes.meta.timestamp,
nonceStr: signaturesRes.meta.nonceStr,
signature: signaturesRes.app.signature,
jsApiList: apis,
}).catch(e => {
console.error(e)
});
console.log('agentConfig res', agentConfigRes);
wx.error(console.error);
};
複製程式碼
本地開發
本地開發應該是所有前端開發都想要的一個理想環境。但是在配置側邊欄應用的 HTML 地址時,你是不能直接填 localhost 的,必須是可信域名!網上有些教程可能會讓你直接改 hosts 檔案來將域名轉向 localhost。
我給出的解決方案是使用 Whistle 來將企業微信的流量代理到本地 localhost,這樣就可以在本地進行開發了,具體操作可看這裡。
# 代理前端(側邊欄頁面代理到本地的 3000 埠),這裡要改為你自己配置H5的地址就好
//service-aj0odbig-1253834571.gz.apigw.tencentcs.com http://127.0.0.1:3000
# 代理後端(後端模板的 baseURL 寫死為 backend.com,這裡代理到本地的 5000 埠)
//backend.com http://127.0.0.1:5000
複製程式碼
不過,在企業微信側邊欄上除錯我們的應用還是很麻煩,我們更希望的是可以直接在瀏覽器上除錯程式,等開發差不多了,再去真實的側邊欄環境下除錯。
考慮到這一點,我在我的前端模板裡也實現 Mock 模式。具體怎麼玩可以看 這裡,可以直接 Mock 客戶端 API 的返回值和使用者身份資訊。能大大提高開發效率。
總結
總的來說,個人覺得雖然側邊欄開發真是個麻煩事,還有好多 bug 和相容問題,但是確實是客戶/社群運營一個非常好用的工具。
