
前言
如果你不知道 企業(yè)微信側(cè)邊欄 是什么,那就可以劃走這篇文章了。如果你知道這是個(gè)啥,那你一定非??鄲酪趺撮_始。
從去年就開始就一直有在做企業(yè)微信側(cè)邊欄的應(yīng)用。說實(shí)話,開發(fā)和調(diào)試體驗(yàn)實(shí)在是太糟糕了,而且上手的時(shí)候跟本連怎么打開它都不知道。
[圖片上傳失敗...(image-39edf-1634365911621)]
最難頂?shù)氖枪俜轿臋n也寫得不仔細(xì),連個(gè)最簡(jiǎn)單能跑的 Demo 都沒有,找開發(fā)討論區(qū)吧,官方的客服也不太懂代碼,問了等于沒問,很多貼子都是網(wǎng)友互助的。
所以為了能幫助更多人上手側(cè)邊欄,我寫了一個(gè) 教程網(wǎng)站,以及 前端 和 后端 兩個(gè) Demo。
[圖片上傳失敗...(image-2393a7-1634365911621)]
這個(gè)教程和 Demo 都放在 這個(gè) Organization 上了,也可以關(guān)注一下。
當(dāng)然本文也不是簡(jiǎn)單的水文,所以下面簡(jiǎn)單來聊聊 企業(yè)微信側(cè)邊欄 一些重要的部分吧。
是什么
企業(yè)微信側(cè)邊欄(下稱企微側(cè)欄)其實(shí)就是企業(yè)微信右邊的一個(gè)側(cè)欄(WebView)。
[圖片上傳失敗...(image-813319-1634365911621)]
但是并不是所有對(duì)話(session)都能打開這個(gè)側(cè)邊欄的,只有在 外部聯(lián)系人 和 外部聯(lián)系群 的對(duì)話中才能右下角打開側(cè)欄的按鈕。
那 外部聯(lián)系人 和 外部聯(lián)系群 又是個(gè)啥?為什么只有在這種情況下才能打開呢?這就要說到側(cè)欄到底要解決的什么問題。
為什么
側(cè)欄真正要解決的痛點(diǎn)其實(shí)是 社群/客戶運(yùn)營(yíng)和管理。
可能大家微信上只有 300 左右的好友,但是有沒有想過如果作為一個(gè)銷售人員,他可能需要添加 1000+ 的客戶,要?jiǎng)?chuàng)建 100+ 的群聊,每天可能都會(huì)收到成百上千條消息。
[圖片上傳失敗...(image-4e84ad-1634365911621)]
這些客戶可能被分為:想買產(chǎn)品的、有意愿想買產(chǎn)品的、已經(jīng)買產(chǎn)品的、普通客戶、VIP、SVIP等。
群聊可能被分為:2020年11月學(xué)習(xí)群、VIP 群、粉絲群等等。
這么多的客戶和群聊,對(duì)于單一個(gè)銷售人員來說就非常頭疼。很容易就忘記這個(gè)客戶是哪個(gè)分區(qū)、哪個(gè)類別、哪種標(biāo)簽的。
而且銷售人員主要的工作就是要精細(xì)化運(yùn)營(yíng)、每天都要和客戶以及群聊 聊天。什么時(shí)候聊、怎么聊、聊什么都是大學(xué)問,而且一旦和這么多客戶、群聊聊天更是難上加難。類比一下,時(shí)間管理大師最多也只能和 10 個(gè)人聊也已經(jīng)頂天了。
所以企業(yè)微信就想:能不能在聊天會(huì)話當(dāng)中有一個(gè)工具箱,銷售人員就可以在這個(gè)工具箱里查看客戶/群聊的業(yè)務(wù)數(shù)組,或者通過這個(gè)工具箱更好地運(yùn)營(yíng)。這就是側(cè)邊欄的由來。
上面的這些 “客戶” 和 “群聊” 則這被稱為:外部聯(lián)系人 和 外部聯(lián)系群,這里的 “外部” 指的就是 不是自己企業(yè)內(nèi)部員工。
側(cè)欄應(yīng)用架構(gòu)
側(cè)邊欄本質(zhì)上也是一個(gè) WebView,所以我們只需要寫好前端,無論是普通 html 或者 SPA App 都能被放在側(cè)邊欄上。
但是普通的前端還是不夠的,如果你想和 企業(yè)微信 進(jìn)行一定的交互,比如發(fā)消息、立即創(chuàng)建群聊、打開個(gè)人信息彈窗,那就需要企業(yè)微信提供的 JS-SDK,具體文檔請(qǐng)看這里。
[圖片上傳失敗...(image-40e761-1634365911621)]
可是 JS-SDK 是需要先 config 才能正常使用,而 config 的參數(shù)需要從 企業(yè)微信服務(wù)端 獲取 jsapi_ticket 來生成 signature 才能正常初始化 JS-SDK。
wx.agentConfig({
corpid: '', // 必填,企業(yè)微信的corpid,必須與當(dāng)前登錄的企業(yè)一致
agentid: '', // 必填,企業(yè)微信的應(yīng)用id (e.g. 1000247)
timestamp: , // 必填,生成簽名的時(shí)間戳
nonceStr: '', // 必填,生成簽名的隨機(jī)串
signature: '',// 必填,簽名,見附錄-JS-SDK使用權(quán)限簽名算法
jsApiList: ['selectExternalContact'], //必填,傳入需要使用的接口名稱
success: function(res) {
// 回調(diào)
},
fail: function(res) {
if(res.errMsg.indexOf('function not exist') > -1){
alert('版本過低請(qǐng)升級(jí)')
}
}
});
然后問題又來了,從 企業(yè)微信服務(wù)端 獲取 jsapi_ticket 又需要 access_token 這個(gè)關(guān)鍵參數(shù),而 access_token 又不能直接緩存到前端,因此,還需要另外一個(gè)后端(中間層)來緩存 access_token,并提供 企業(yè)微信服務(wù)端 API 的轉(zhuǎn)發(fā)服務(wù)。
所以,總得來說,側(cè)邊欄看似是前端的東西,但其實(shí)它的基礎(chǔ)架構(gòu)起碼有 側(cè)邊欄、業(yè)務(wù)服務(wù)端 和 企微服務(wù)端。
[圖片上傳失敗...(image-386d6e-1634365911621)]
企微的服務(wù)端已經(jīng)由企業(yè)微信提供了,那我們要實(shí)現(xiàn)的就是 側(cè)邊欄 和 業(yè)務(wù)服務(wù)端 了。如果你是第一次搞側(cè)邊欄,一定會(huì)被弄得非常煩,所以建議 Fork 我的 側(cè)邊欄(前端)模板 和 后端模板,然后在這基礎(chǔ)上進(jìn)行修改。
開發(fā)關(guān)鍵部分
因?yàn)檫@里面的細(xì)節(jié)太多了,想了解具體實(shí)現(xiàn)還是去看那兩個(gè)模板,這里僅講一些比較重要的點(diǎn)。
轉(zhuǎn)發(fā)服務(wù)
首先來說這個(gè)轉(zhuǎn)發(fā)服務(wù)吧,需要對(duì) 企微服務(wù)端 API 進(jìn)行轉(zhuǎn)發(fā),而服務(wù)端的請(qǐng)求是需要 access_token 作為重要參數(shù)來轉(zhuǎn)的,但是 access_token 不能一直請(qǐng)求獲取,所以需要進(jìn)行 redis 緩存。
而 redis 又需要 Docker 來啟動(dòng),不得不說為了做個(gè)簡(jiǎn)單網(wǎng)頁,連 Docker 都整上了:
version: '3'
services:
redis:
image: redis
container_name: 'wecom-sidebar-redis'
ports:
- "6379:6379"
restart: always
轉(zhuǎn)發(fā)服務(wù)在模板里我使用簡(jiǎn)單的 Koa 實(shí)現(xiàn),redis 客戶端的 NPM 包,可以使用 ioredis 來緩存。
轉(zhuǎn)發(fā)就很簡(jiǎn)單了,直接使用 axios 就可以了,但是要注意 proxy 要設(shè)置為 false,不然會(huì)報(bào)錯(cuò):
const axios = require('axios')
const baseURL = 'https://qyapi.weixin.qq.com/cgi-bin';
const proxy = axios.create({
baseURL,
proxy: false // 不指定會(huì)報(bào)錯(cuò) SSL routines:ssl3_get_record:wrong version number,參考:https://github.com/guzzle/guzzle/issues/2593
})
module.exports = proxy
具體的轉(zhuǎn)發(fā)調(diào)用 API 相信大家都會(huì)怎么寫就不多說了。
重定向獲取 userId
這種 userId 的獲取機(jī)制和微信網(wǎng)頁開發(fā)是差不多的,需要先重定向某個(gè) url,然后從 search 參數(shù)獲取 code,再用這個(gè) code 通過上面的轉(zhuǎn)發(fā)服務(wù)向企業(yè)微信服務(wù)端換取 userId,具體實(shí)現(xiàn)可以看 文檔這里。
[圖片上傳失敗...(image-d6b307-1634365911621)]
/**
* 獲取重定位的 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`;
};
/**
* 判斷當(dāng)前網(wǎng)頁是否需要重定向
*/
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 初始化
這應(yīng)該是使用客戶端 API 時(shí)最重要的一個(gè)步驟了,而企微的 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,
});
});
};
然后從我們的轉(zhuǎn)發(fā)服務(wù)獲取 signature 并調(diào)用 agentConfig(3.0.24 以上可以不調(diào)用 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);
};
本地開發(fā)
本地開發(fā)應(yīng)該是所有前端開發(fā)都想要的一個(gè)理想環(huán)境。但是在配置側(cè)邊欄應(yīng)用的 HTML 地址時(shí),你是不能直接填 localhost 的,必須是可信域名!網(wǎng)上有些教程可能會(huì)讓你直接改 hosts 文件來將域名轉(zhuǎn)向 localhost。
我給出的解決方案是使用 Whistle 來將企業(yè)微信的流量代理到本地 localhost,這樣就可以在本地進(jìn)行開發(fā)了,具體操作可看這里。
# 代理前端(側(cè)邊欄頁面代理到本地的 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
不過,在企業(yè)微信側(cè)邊欄上調(diào)試我們的應(yīng)用還是很麻煩,我們更希望的是可以直接在瀏覽器上調(diào)試程序,等開發(fā)差不多了,再去真實(shí)的側(cè)邊欄環(huán)境下調(diào)試。
考慮到這一點(diǎn),我在我的前端模板里也實(shí)現(xiàn) Mock 模式。具體怎么玩可以看 這里,可以直接 Mock 客戶端 API 的返回值和用戶身份信息。能大大提高開發(fā)效率。
總結(jié)
總的來說,個(gè)人覺得雖然側(cè)邊欄開發(fā)真是個(gè)麻煩事,還有好多 bug 和兼容問題,但是確實(shí)是客戶/社群運(yùn)營(yíng)一個(gè)非常好用的工具。
希望這篇文章能帶給大家側(cè)邊欄一些基礎(chǔ)概念,想了解更多可以去 我的教程 深入上手。