玩不转的企业微信侧边栏

前言

如果你不知道 企业微信侧边栏 是什么,那就可以划走这篇文章了。如果你知道这是个啥,那你一定非常苦恼要怎么开始。

从去年就开始就一直有在做企业微信侧边栏的应用。说实话,开发和调试体验实在是太糟糕了,而且上手的时候跟本连怎么打开它都不知道。

[图片上传失败...(image-39edf-1634365911621)]

最难顶的是官方文档也写得不仔细,连个最简单能跑的 Demo 都没有,找开发讨论区吧,官方的客服也不太懂代码,问了等于没问,很多贴子都是网友互助的。

所以为了能帮助更多人上手侧边栏,我写了一个 教程网站,以及 前端后端 两个 Demo。

[图片上传失败...(image-2393a7-1634365911621)]

这个教程和 Demo 都放在 这个 Organization 上了,也可以关注一下。

当然本文也不是简单的水文,所以下面简单来聊聊 企业微信侧边栏 一些重要的部分吧。

是什么

企业微信侧边栏(下称企微侧栏)其实就是企业微信右边的一个侧栏(WebView)。

[图片上传失败...(image-813319-1634365911621)]

但是并不是所有对话(session)都能打开这个侧边栏的,只有在 外部联系人外部联系群 的对话中才能右下角打开侧栏的按钮。

外部联系人外部联系群 又是个啥?为什么只有在这种情况下才能打开呢?这就要说到侧栏到底要解决的什么问题。

为什么

侧栏真正要解决的痛点其实是 社群/客户运营和管理

可能大家微信上只有 300 左右的好友,但是有没有想过如果作为一个销售人员,他可能需要添加 1000+ 的客户,要创建 100+ 的群聊,每天可能都会收到成百上千条消息。

[图片上传失败...(image-4e84ad-1634365911621)]

这些客户可能被分为:想买产品的、有意愿想买产品的、已经买产品的、普通客户、VIP、SVIP等。

群聊可能被分为:2020年11月学习群、VIP 群、粉丝群等等。

这么多的客户和群聊,对于单一个销售人员来说就非常头疼。很容易就忘记这个客户是哪个分区、哪个类别、哪种标签的。

而且销售人员主要的工作就是要精细化运营、每天都要和客户以及群聊 聊天。什么时候聊、怎么聊、聊什么都是大学问,而且一旦和这么多客户、群聊聊天更是难上加难。类比一下,时间管理大师最多也只能和 10 个人聊也已经顶天了。

所以企业微信就想:能不能在聊天会话当中有一个工具箱,销售人员就可以在这个工具箱里查看客户/群聊的业务数组,或者通过这个工具箱更好地运营。这就是侧边栏的由来。

上面的这些 “客户” 和 “群聊” 则这被称为:外部联系人外部联系群,这里的 “外部” 指的就是 不是自己企业内部员工

侧栏应用架构

侧边栏本质上也是一个 WebView,所以我们只需要写好前端,无论是普通 html 或者 SPA App 都能被放在侧边栏上。

但是普通的前端还是不够的,如果你想和 企业微信 进行一定的交互,比如发消息、立即创建群聊、打开个人信息弹窗,那就需要企业微信提供的 JS-SDK,具体文档请看这里

[图片上传失败...(image-40e761-1634365911621)]

可是 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 的转发服务

所以,总得来说,侧边栏看似是前端的东西,但其实它的基础架构起码有 侧边栏业务服务端企微服务端

[图片上传失败...(image-386d6e-1634365911621)]

企微的服务端已经由企业微信提供了,那我们要实现的就是 侧边栏业务服务端 了。如果你是第一次搞侧边栏,一定会被弄得非常烦,所以建议 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,具体实现可以看 文档这里

[图片上传失败...(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`;
};

/**
 * 判断当前网页是否需要重定向
 */
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 和兼容问题,但是确实是客户/社群运营一个非常好用的工具。

希望这篇文章能带给大家侧边栏一些基础概念,想了解更多可以去 我的教程 深入上手。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容