Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

WeChat 公众号/小程序能力

oak-general-business 里最复杂的一块通用能力,几乎一定是微信生态。

这里不是只有“微信登录”这么简单,而是同时覆盖了:

  • 网页微信登录;
  • 小程序登录;
  • 公众号网页登录;
  • 公众号回调;
  • 微信用户绑定;
  • 微信二维码;
  • 菜单管理;
  • 公众号标签;
  • 微信模板;
  • 素材管理;
  • 短信跳小程序 openlink。

如果把这些能力都混在一起看,会非常乱。所以这一章的重点,是把它们在 Oak 里的边界讲清楚。

主要对象

微信相关的主要实体包括:

  • WechatUser
  • WechatLogin
  • WechatQrCode
  • WechatMenu
  • WechatPublicTag
  • UserWechatPublicTag
  • WechatTemplate
  • WechatPublicAutoReply
  • WechatMpJump

此外,很多微信能力其实还依赖 Application.config 里的微信配置,所以 Application 仍然是微信能力的根配置入口。

组件

这一章涉及的组件非常多,常见的有:

  • src/components/passport/wechatMp
  • src/components/passport/wechatMpForWeb
  • src/components/passport/wechatPublic
  • src/components/passport/wechatPublicForWeb
  • src/components/wechatLogin/qrCode
  • src/components/wechatLogin/confirm
  • src/components/wechatUser/login
  • src/components/wechatUser/bindingList
  • src/components/wechatUser/unbindBtn
  • src/components/wechatQrCode/scan
  • src/components/wechatQrCode/share
  • src/components/wechatMenu/*
  • src/components/wechatPublicTag/*
  • src/components/userWechatPublicTag/*
  • src/components/wechatMaterialLibrary
  • src/components/wechatPublicAutoReply
  • src/components/common/weChatLoginGrant
  • src/components/common/weChatLoginQrCode

从这些组件命名就能看出来,微信能力在这个包里已经不只是“一个登录按钮”了。

wechatUser/login 常用参数

这个组件本身几乎不要求项目层传很多东西,最常见的就是:

  • code
  • state
  • oakPath

taicang/src/pages/frontend/wechatUser/login/web.pc.tsxhaina-busi/src/pages/wechat/wechatUser/login/web.pc.tsx 的写法都很薄:

<WechatUserLogin code={code} state={state} oakPath="$wechatUser/login-cpn" />

它内部会自己读取当前 URL 上的 code / state,调用 features.token.loginWechat(...),成功后按 state 跳回业务页。

wechatQrCode/scan 常用参数

扫码组件最关键的两个参数是:

  • scene:小程序码扫码场景值
  • q:普通链接二维码携带的值

组件内部会把它们展开成二维码记录 id,然后按二维码里保存的 pathname / props / state 自动跳转。

自定义微信授权确认页

除了公共组件,haina-busi 里还有一个很值得参考的项目层包装:components/wechatLogin/confirm。它会先拼好公众号 OAuth 地址,再把 redirectUri 指向:

  • /wechat/wechatUser/login

这种做法适合项目需要在真正跳微信授权前,先展示一层“确认登录到哪个业务对象”的页面。

wechatUser/bindingList / wechatUser/unbindBtn

这两个组件更偏“账号绑定管理”,通常不会放在登录页,而会放在:

  • 个人中心
  • 账号安全页
  • 后台用户详情页

wechatUser/bindingList 的真实行为很轻量:

  • 读取 wechatUser 列表
  • 只保留已经绑定到本地用户的记录,也就是 userId 不为空的项

所以它适合做“我当前绑定了哪些微信身份”的列表视图,而不是通用的微信用户管理页。

wechatUser/unbindBtn 更简单,它只读取当前 wechatUser.iduserId,作用就是给解绑按钮提供当前记录上下文。项目层通常会把它和:

  • unbindingWechat

这类 aspect 动作一起用,而不是自己去查 wechatUser 再手工拼按钮。

公众号后台管理组件

除了登录和扫码,oak-general-business 还把公众号后台常用能力拆成了几组现成组件。它们最适合直接挂在 application/panel 自动生成的微信页签里。

wechatMenu

这个组件最关键的参数是:

  • applicationId
  • tabKey

它在进入页面时会先拉当前 applicationId 下、没有 wechatPublicTagId 的默认菜单,并把 menuId 放到内部状态里。也就是说,它默认管理的是“公众号主菜单”,不是泛化的任意菜单树。

wechatPublicTag/list

这个列表组件的关键参数只有:

  • applicationId

但它内部已经带了几个实际动作:

  • 单条 sync
  • oneKeySync
  • 删除标签

这意味着项目后台如果只是做公众号标签同步,不需要自己调用 feature 拼一个管理页,直接复用它就够了。

userWechatPublicTag/subscribedList

这也是公众号运营后台里很实用的一个组件,关键参数同样只有:

  • applicationId

它默认只查:

  • origin: 'public'
  • subscribed: true

所以它适合做“已关注用户与标签”的运营列表,而不是泛化的全部微信用户列表。它内部还能直接调用:

  • userWechatPublicTag.tagging(...)
  • userWechatPublicTag.syncToLocale(...)

wechatMaterialLibrary

这个组件最重要的参数是:

  • applicationId
  • type
  • getMenuContent

其中 type 决定它去拉:

  • news 图文素材
  • 其它永久素材类型,例如 imagevoicevideo

它内部会直接调 features.wechatMenu.batchGetArticle(...)batchGetMaterialList(...)createMaterial(...)getMaterial(...),所以非常适合给菜单编辑、自动回复编辑器或图文选择弹窗做素材选择面板。

wechatPublicAutoReply

这个组件也只要求:

  • applicationId

但它有一个很关键的默认行为:如果当前公众号还没有自动回复配置,ready() 阶段会自动补一条:

  • type: 'text'
  • event: 'subscribe'

所以项目层不需要担心“第一次进入页面没有任何回复配置时界面空掉”,公共组件已经把首条默认记录准备好了。

前端 feature 与 aspect

前端直接可用的微信相关 feature 主要有:

  • features.token
  • features.wechatSdk
  • features.wechatMenu
  • features.wechatPublicTag
  • features.userWechatPublicTag
  • features.template

这里要特别注意两点:

  • 微信登录虽然是“微信能力”,但前端真正调用的通常是 features.token.loginWechatMp()features.token.loginByWechatInWebEnv(...) 这一层;
  • 微信模板同步虽然属于微信生态,但前端入口并不是单独的 wechat feature,而是 features.template

对应的 aspect 也很丰富,主要包括:

  • 登录相关:loginWechatloginWechatMploginWechatNativeloginByWechat
  • 用户同步相关:syncUserInfoWechatMprefreshWechatPublicUserInfogetWechatMpUserPhoneNumbersetUserAvatarFromWechat
  • 微信登录中间态:createWechatLogin
  • 微信解绑:unbindingWechat
  • 二维码:getMpUnlimitWxaCode
  • 菜单:getCurrentMenugetMenucreateMenucreateConditionalMenudeleteConditionalMenudeleteMenu
  • 标签:createTaggetTagseditTagdeleteTagsyncTagoneKeySync
  • 用户标签:getTagUsersbatchtaggingbatchuntagginggetUserTagsgetUserstaggingsyncToLocalesyncToWechat
  • openlink:wechatMpJump
  • 微信素材与模板:signatureJsSDKuploadWechatMediabatchGetArticlegetArticlebatchGetMaterialListgetMaterialdeleteMaterialsyncWechatTemplate

这已经是一整套相当成型的微信业务中台能力了。

endpoint

这一章必须单独强调真实暴露出来的 endpoint 名称,而不只是文件名。

src/endpoints/wechat.ts 里注册了三个 endpoint:

  • wechatPublicEvent
  • wechatMpEvent
  • wechatMaterial

其中:

  • wechatPublicEvent 同时包含 GET 验证接口和 POST 回调接口;
  • wechatMpEvent 同时包含 GET 验证接口和 POST 回调接口;
  • wechatMaterial 用于读取素材二进制内容,通常会被素材库、菜单预览等界面间接使用。

src/endpoints/index.ts 则在导出这些 endpoint 的同时,额外导出了:

  • registerWeChatPublicEventCallback

它允许项目层在不改公共包 endpoint 主流程的情况下,继续挂接自己的公众号事件处理逻辑。

这些 endpoint 共同承担了微信服务器回调入口,用于处理:

  • 公众号事件;
  • 小程序事件;
  • 扫码、订阅、菜单点击等回调。

也正因为有这一层 endpoint,WechatLoginWechatQrCodeWechatUser 等对象之间的联动才真正成立。

后台规则

后台规则主要散落在这些 trigger / checker 里:

  • triggers/wechatLogin.ts:创建登录中间态时自动生成二维码,过期时同步二维码过期;
  • triggers/wechatQrCode.ts:按二维码类型真正生成公众号二维码、小程序码或跳转链接;
  • triggers/wechatMenu.ts:删除个性化菜单前调用微信删除接口;
  • triggers/wechatPublicTag.ts:删除标签前同步删除微信端标签并清理关联数据;
  • triggers/wechatMpJump.ts:创建 WechatMpJump 时生成 openlink;
  • checkers/wechatPublicTag.tscheckers/wechatQrCode.ts:校验微信标签和二维码数据。

这一套规则说明,微信相关对象并不是“你手工写好数据再去用”,很多关键字段和远端副作用都是由 trigger 自动完成的。

注入点

微信能力的注入点非常明确:

  • 前端在 create(...) 时注入 wechatSdkwechatMenuwechatPublicTaguserWechatPublicTag
  • 登录相关能力通过 features.token 暴露给页面和组件;
  • 模板同步能力通过 features.template 暴露给页面和组件;
  • initialize(...) 在 web 环境下调用 features.wechatSdk.setLandingUrl(window.location.href)
  • initialize(...) 在小程序环境下会按需自动登录;
  • 后端通过 ogb0Aspectsogb0Checkersogb0Endpointsogb0Triggers 合并微信相关能力;
  • 如果项目需要补充公众号或小程序事件处理,直接从 src/registry.backend.ts 注册 registerWeChatPublicEventHandler(...) / registerWeChatPublicEventAfterHandler(...)registerWeChatMpEventHandler(...) / registerWeChatMpEventAfterHandler(...);主处理器先于默认逻辑执行,after 处理器在默认逻辑后补充收尾。

换句话说,微信生态的前后台装配都已经在 oak-general-business 里设计好了。

真实项目里的接法

taicanghaina-busi 实际都采用了一条统一链路:

  1. 登录页或业务页里的 user/loginredirectUri 指向 /wechatUser/login
  2. /wechatUser/login 页面只包 wechatUser/login
  3. 二维码场景再单独提供 /wechatQrCode/scan
  4. 公众号或小程序回调最终还是落回 features.token

这样做的好处是,项目层不用到处自己处理 code/state,而是统一交给公共组件。

项目中如何接入

微信能力在项目里的接入,通常分成三段:

  • Application.config 里配好 wechatMp / wechatPublic / web.wechat
  • 初始化时执行 initializeOgb0Features(...),让 wechatSdktokentemplateapplication 这些 feature 串起来
  • 后台把 ogb0Aspects / ogb0Checkers / ogb0Triggers / ogb0Endpoints 合并进去,真正暴露微信回调入口并接上默认规则

所以项目层真正要关心的,通常是“应用配置是否正确”“路由和回调地址是否对上”,而不是从零写微信协议处理。

使用示例

1. Web 端调用微信 JSSDK

oak-general-business/src/features/wechatSdk.web.ts 已经把签名和 wx.config 包好了,项目里直接这样用:

await this.features.wechatSdk.loadWxAPi('chooseImage', { count: 1 });
await this.features.wechatSdk.loadWxAPi('scanQRCode', { needResult: 1 });

通常不需要页面自己再去调用 signatureJsSDK(...),因为 WechatSdk feature 会先完成签名初始化。

2. 管理公众号标签和模板

这些能力已经有直接可调用的 feature:

const applicationId = this.features.application.getApplication().id!;

await this.features.wechatPublicTag.createTag({
  applicationId,
  name: '核心用户',
});

await this.features.template.syncWechatTemplate(applicationId);

3. 小程序端显式触发登录

如果项目关闭了自动登录,也可以手工调用:

await this.features.token.loginWechatMp();

4. 项目侧补充公众号事件处理

如果默认的 wechatPublicEvent 流程之外,你还要补项目自己的事件逻辑,可以注册带 msgType / event 的处理器:

import {
  registerWeChatPublicEventAfterHandler,
  registerWeChatPublicEventHandler,
} from '@oak-general-business/registry.backend';

registerWeChatPublicEventHandler({
  applicationId: 'wx-public-app-id',
  msgType: 'event',
  event: 'subscribe',
  handler: async ({ data, context }) => {
    // 项目自己的补充逻辑
  },
});

registerWeChatPublicEventAfterHandler({
  applicationId: 'wx-public-app-id',
  msgType: 'event',
  event: 'subscribe',
  handler: async ({ response }) => {
    // 默认逻辑后的收尾处理
  },
});

5. 项目侧补充小程序事件处理

小程序回调也走同一套注册表:

import { registerWeChatMpEventHandler } from '@oak-general-business/registry.backend';

registerWeChatMpEventHandler({
  applicationId: 'wx-mp-app-id',
  msgType: 'event',
  event: 'subscribe',
  handler: async ({ data }) => {
    // 小程序项目自己的补充逻辑
  },
});

6. 用统一扫码页承接二维码跳转

如果项目里有活动页、分享页、落地页需要二维码跳转,推荐像 taicang / haina-busi 一样单独挂一个扫码页:

<WechatQrCodeScan
  scene={scene}
  q={q}
  oakPath="$wechatQrCode/scan-cpn"
/>

这样二维码失效、非法二维码、跳转目标解析这些细节都会由公共组件统一处理。

使用建议

最推荐的理解方式是按链路拆:

  • 登录链路:Passport/ApplicationPassport + Token aspect + WechatLogin
  • 用户绑定链路:WechatUser
  • 二维码链路:WechatQrCode
  • 管理后台链路:WechatMenu / WechatPublicTag / UserWechatPublicTag
  • 素材模板链路:WechatTemplate + 素材相关 aspect

这样读,你会发现这并不是零散功能,而是围绕微信生态拆出来的一整层业务基础设施。