「企业微信协议登录接口」这个搜法里藏着一个误解:以为存在一个 login 端点,传几个参数进去就登上了。实际链路里没有这样一个端点,也不该有 —— 登录是一个跨越几十秒、需要真人参与、而且随时可能失效的过程,任何把它压缩成一次同步调用的设计,都会在第一次掉线时把成本连本带利还回来。这篇讲这个过程该怎么建模、凭证和登录态为什么必须分开,以及自愈到哪一步就走不下去了。下面按 wecomapi 这类托管式接入的形态来说明。
先把三样东西彻底分开
登录相关的 bug 有一大半源于同一个错误:把三样性质完全不同的东西塞进了同一个叫 token 的变量里。
- 调用凭证:证明「你这个系统有权调用接口」。程序可持有、可轮换、可提前刷新,全程不需要人参与。
- 账号登录态:证明「这个账号此刻处于可用会话中」。有生命周期、会失效,失效时机不由你的代码决定。
- 扫码动作:把一个真人的授权带进上面第二项的唯一途径。它天然不可程序化 —— 这恰恰是它作为授权手段的意义所在。
分开之后,两类失效的处理方式立刻显出差别,而且方向是相反的:凭证失效,刷新一下就好,属于程序内可闭环的事;账号掉线,重试一万次也不会好,必须有人做点什么。混在一起的直接后果是重试逻辑写错方向 —— 对着一个需要重新扫码的账号退避重试整晚,监控面板全绿,客户那边两小时没人回。
一个很具体的检验:让你的系统回答「现在是凭证不对,还是账号掉了」。如果它只能告诉你「调用失败了」,说明这三样还没分开,后面几节做什么都是白搭。
扫码是一个短生命周期状态机
从发起登录到会话建立,中间会经过几个明确的阶段:请求登录、拿到一张有时效的二维码、等待扫描、已扫描待确认、确认后会话建立;旁路上还有二维码过期和用户取消两个终态。把它建成状态机不是为了好看,是因为这几个状态各自对应完全不同的界面文案和后续动作。
三条工程要求,每一条都对应一个真实发生过的运维投诉。第一,二维码有时效,过期后界面必须能原地换一张,而不是让人退出去重来一遍 —— 后者在多账号场景下能把一次两分钟的操作拖成半小时。第二,「已扫描待确认」这个中间态一定要展示出来;不展示的话,扫完手机上没反应的人会反复扫,反复扫又会打断流程,最后变成「扫了五次都不行」。第三,结果要以状态查询或事件为准,不要以「页面上出现了二维码」为准 —— 二维码出现只说明请求成功了,和登录成不成功是两件事。
拿状态的方式有两种:轮询实例状态,或者订阅状态变更事件。就扫码这一段而言,能订阅就别轮询 —— 轮询的间隔永远卡在「太慢」和「太吵」之间,调不出一个让所有人满意的值(长期的在线监测是另一回事,那里两条都得有)。用 wecomapi 的事件回调拿到状态变更之后,登录页只需要被动刷新,处理上仍然遵循先快速 ACK 再异步落库那套约定;可订阅的事件类型以文档为准。
// 示意代码,状态语义与可订阅的事件类型以 wecomapi 文档为准
type LoginPhase =
| "requesting" // 已发起,二维码还没拿到
| "waiting" // 二维码已展示,等人扫
| "scanned" // 扫到了,等对方在手机上确认 —— 这个态必须让人看见
| "online" // 会话建立,进入调度池
| "expired" // 二维码超时:原地换一张,不是退出去重来
| "cancelled"; // 人主动取消,不要自动重发
onPhase("expired", () => refreshQrCode(instanceId));
// 掉线的分叉点只有一个问题:这事需不需要人
onPhase("offline", (reason) =>
needsHuman(reason) ? pageOwner(instanceId) : backoffProbe(instanceId));不要指望一个 login(账号, 密码)
经常有人问能不能绕开扫码。答案是不该按这个思路设计:授权这一步需要真人在自己的设备上确认,这是账号安全模型的一部分,不是接入方式的缺陷。工程上正确的动作是承认「人在环路里」这个前提,然后把这件事的成本压到最低。
压成本有三个方向,按性价比排序是:减少重建次数 > 缩短单次重建耗时 > 提高自动恢复覆盖率。多数团队只做第三个 —— 保活、探活、自动重连 —— 而前两个几乎不花钱:环境稳定、变更克制、联调用独立账号。托管侧承担的是「维持」,不是「重建」;把保活与异常恢复交给 wecomapi 这类平台之后,你要优化的重点就从「怎么不掉」变成「掉了之后多快能找到那个拿手机的人」。
还有一条:「登录态能维持多久」这个数不该写进设计假设。它受平台策略、账号自身操作、网络与设备环境影响,任何一个具体数字都会在某天失效,围绕它排的定时任务会跟着一起失效。把系统建成「随时可能掉、掉了有分级、需要人时找得到人」,可用性才不依赖任何时长承诺。
联调阶段最容易烧掉登录态的三个习惯
上线之后的掉线多半是环境问题,上线之前的掉线基本都是自己作的。下面三条按出现频率排。
- 1拿生产账号联调。开发期会频繁重启、切环境、改回调地址,每一次都是一次风险;一旦生产号被搞掉线,业务中断的成本远高于多开一个测试实例。按账号订阅的计价方式下,这笔账很容易算清楚。
- 2「不对就删了重建」。在无状态系统里重建是最快的修复手段,在这里是最慢的 —— 它把一个可能自动恢复的问题,换成一次必须找人的操作。重建之前先确认自动恢复这条路真的走不通。
- 3把发版排在没人在的时段。发版、迁移、环境切换、密钥轮换只要有可能打断会话,就得挑有人能扫码的时间做。半夜发版、早上发现三个号要重扫,这个组合每个团队都会经历一次。
还有一条不算习惯但同样常见:多环境共用一个账号,测试发出去的消息进了真实客户的会话。这类事故一次就够。实例按环境隔离的具体做法站内另有一篇,这里只强调一句:环境维度的隔离不能省,省下来的那点成本第一次事故就还回去了。
自愈的边界:解决动作里有没有人
「自愈」这个词被用得很松,容易让人以为掉线都能自动扛过去。判据站内讲掉线自愈那篇已经给过 —— 看解决动作里有没有人;掉线怎么分级、告警怎么抑制、任务怎么挂起、恢复之后怎么对账,也都在那篇里,这里不重复。
真正被普遍做漏的是边界另一侧 —— 「需要人」的那部分几乎从来没被当成一条正经流程设计过,结果就是自愈做得很漂亮,剩下那一小部分拖了两个小时。这一节只补两件登录独有、通用自愈流程里补不上的事。
- 1二维码要能送到人手上。通知里必须带一个点开就能扫的入口,而不是一句「请登录控制台查看」。多一步跳转,恢复时间就多几分钟,而这几分钟全在业务中断里。
- 2得指定谁去扫。账号的归属人要落在数据里,不能靠在群里问「这个号是谁的」。没有归属人字段的多账号系统,掉线恢复时长基本等于运气。
还有一个落点容易被忽略:状态面板是给人查的,不是告警。wecomapi 控制台能看到实例当前状态,但没有人会一直盯着面板;把状态变更接进你自己的告警链路、让它在分钟级推送到值班的人,这一步始终要自己做。面板解决的是「我想查的时候能查到」,告警解决的是「我没想查的时候它来找我」,两者不能互相替代。
上线前的四个问题
不用读文档,对着自己的系统问一遍就行。任何一个答不上来,对应那一节就还没做完。
- 1调用失败时,你的系统能不能区分「凭证问题」和「账号掉线」?错误分类停在哪一层,恢复策略就只能精确到哪一层。
- 2二维码过期之后,运维要点几下才能拿到新的一张?超过三下,说明这个界面是按「一次性接入」设计的,不是按「长期运营」设计的。
- 3一个账号掉线,通知在几分钟内到达具体的人?到的是一个群、一个邮箱,还是一个有排班的值班人?
- 4上一次发版有没有影响任何一个账号的登录态?如果没有人专门去看过,那就是还没验证,不能算作「没影响」—— 这两句话在复盘会上的分量完全不同。
本文讲的是链路顺序、状态建模与运维边界,不涉及任何具体接口定义。精确的字段、状态语义、事件类型与端点以 wecomapi 线上文档为准,示意代码只用于表达阶段关系。
常见问题
- 有没有一个接口能直接传账号密码登录?
- 不要按这个思路设计。扫码之所以是扫码,就是因为授权这一步需要真人在自己的设备上确认,这是账号安全模型的一部分,不是接入方式的缺陷。工程上该做的是承认「人在环路里」这个前提,把重建登录态的次数降到最低、把必须找人的那条流程做顺,而不是找一条绕过它的路。
- 登录态大概能维持多久?
- 这个数不该被写进设计假设。它受平台策略、账号自身操作、网络与设备环境影响,任何一个具体数字都会在某天失效,围绕它排的定时任务也会跟着失效。正确的做法是把系统建成「随时可能掉、掉了有分级、需要人时找得到人」,让可用性不依赖任何一个时长承诺。
- 怎么知道一个账号现在到底是不是在线?
- 以平台侧的状态为准,不要靠「上一次调用成功了」来推断 —— 那只能说明过去某个时刻可用。工程上推荐订阅状态变更事件、在本地维护一份带时间戳和原因的状态副本,调度前读本地副本;同时保留一条定期主动查实例状态的探活兜底,事件漏投和状态漂移各有各的盲区,只做其中一条迟早会踩空。具体的状态取值与可订阅事件以 wecomapi 文档为准。
准备好动手了?
精确字段、鉴权与端点以线上文档为准;可在控制台创建密钥后联调。
